CAT实时监控平台从零部署到Java客户端接入实践
发布时间:2026/10/19 19:23:52 锦皓数字建站

排障搞到凌晨一点接口的 P99 从 200 毫秒涨到 1.2 秒线上查了一圈MySQL 慢查询没发现、Redis 命中率正常、消息队列积压为零就是找不到瓶颈。最后是某同事提议把链路里的每一个外部调用都拉出来看逐一排查才在一个旧网关的 HTTP 调用上抓到异常。这件事之后团队决定把实时监控补上选型时就盯上了开源社区里常被简称为 CAT 的实时监控平台。这篇文章就是我在两套环境里把 CAT 从零装起来并接进模拟项目 X 的全过程服务端搭建的两种路线、Java 客户端接入的完整配置、埋点 API 怎么用以及我实际踩过的六个坑。适合正在选型 APM、或者已经确定用 CAT 但卡在安装配置阶段的同学。先说清楚CAT 不是那种开箱即看的玩具监控装起来有点门槛但只要把链路理顺它在生产环境里能给你的排查效率带来质的提升。1. 为什么我不选轻量 APM而是选择了 CAT1.1 CAT 到底解决了什么问题CAT 的定位是实时应用监控平台核心能力集中在三块第一分钟级别的实时性能报表不用等聚合任务跑完刷新页面就能看到最近几秒内的调用情况第二非常灵活的自定义埋点体系可以精确到某个 URL、某个 SQL、某个缓存操作甚至可以给业务埋点打上标签第三链路追踪与 Problem 定位它能把一次请求经过的每个环节按树形展开哪个节点耗时高一眼看穿。这块能力和网上常见的轻量 APM 有重叠但 CAT 更偏向自己掌控数据数据由客户端采集后直接上报到你的服务器不经过任何第三方平台。对很多中小团队来说监控数据留在自己手里后续对接告警或者做二次分析都方便得多。而且它的告警能力是内置的阈值、恢复、持续告警时间都可以自定义不需要额外再搭一套规则引擎。1.2 选型对比与适用边界我建了个小表格把当时纠结过的三类方案放在一起对比各位可以代入自己的场景感受一下能力维度CAT轻量开源自建 APM商业监控产品数据实时性秒级聚合报表刷新快分钟级甚至更慢秒级但依赖网络回传自定义埋点能力强API 灵活一般通常只做自动埋点弱基本靠预设链路追踪深度支持需要配合埋点习惯开箱即用自动生成链路强但偏黑盒部署运维成本较高依赖 MySQL、HDFS 选配较低单进程就能跑零部署按量付费掌控度数据、规则、存储都在自己手里数据在自己手里能力有限数据在别人手里我们当时选 CAT 的判断依据其实很简单团队是 Java 技术栈有一个复杂的核心链路会经过多个内部服务而且我们有明确的业务指标埋点诉求比如 下单转化率、支付回调延迟 这类需要自定义维度的数据。轻量 APM 自带链路是方便但自定义埋点能力太弱商业产品又绕不开数据外送的问题。CAT 恰好把两边需求都覆盖了。不过也要说清适用边界如果你的目标仅仅是快速拿到一个漂亮的调用拓扑图不想花太多精力维护服务端那 CAT 对你来说是重了一半。它的安装和维护确实比轻量 APM 要繁琐我在下面的第二章会讲到它依赖 MySQL集群模式下还可能用到 HDFS 做历史报表存储。这些都需要额外的运维精力去支撑。2. 安装 CAT 之前先花十分钟理清架构和端口2.1 客户端、服务端、控制台各管什么安装之前最怕的就是稀里糊涂把服务端跑起来然后发现完全不知道自己在装什么。CAT 的整体运行逻辑其实可以拆成四块客户端模块cat-client以 JAR 包方式嵌进你的 Java 应用负责埋点采集、本地聚合、异步上报。它会在应用内部维护一个缓冲队列把埋点数据批量发到服务端避免每次打点都发生网络交互。服务端接收模块cat-consumer接收客户端上报的数据做实时统计与分析生成报表数据。多个节点可以组成集群分担压力。控制台模块cat-home承载 Web 界面提供报表展示、告警管理、配置下发。通常和接收模块部署在同一进程里一个 JVM 实例同时承担这两个角色。存储层MySQL 保存元数据和基础配置比如机器信息、告警配置、报表定义HDFS 是可选的用于保存历史报表文件如果不需要留存太久的数据不装 HDFS 也能跑只是报表保留时间会受限。端口这块我专门记了一份小抄端口用途说明8080控制台 Web 端口浏览器访问 dashboard 用的2280客户端数据上报端口cat-client 往服务端打数据的 TCP 通道3306MySQL 端口服务端连接数据库使用这里最容易踩的坑是防火墙只放行了 8080忘了 2280。服务端 Web 界面看着一切正常但所有客户端都连不进来报表数据完全为空排查半天最后才发现是底层上报端口被防火墙挡了。所以装完第一件事就是确认 2280 端口能被客户端所在机器访问到。2.2 环境准备清单我准备了一张自检清单在动手前过一遍能省掉后面非常多麻烦一台 Linux 服务器内存 4G 起步。内存太小的话控制台和接收模块跑在同一个 JVM 里容易触发频繁 GC报表刷新很卡。JDK 1.8 或更高版本优先使用 JDK 8 匹配较为稳妥。装完执行java -version确认。MySQL 5.7 或 8.0准备一个空数据库建议数据库字符集用utf8mb4避免中文描述乱码。时间同步确认。执行date看下系统时间和实际时间是否一致差太多会导致客户端上报的数据被归到错误的时间段后续报表显示很难看。防火墙和安全组放行 8080、2280 这两个端口。另外建议提前规划好存储位置。CAT 服务端运行时会读一个固定的数据目录我用的是/data/appdatas/cat里面放配置文件还有日志。这个目录用户的权限一定给足否则启动时静默报错报表页面经常白屏。后面我在第五章里会细讲。3. 服务端搭建Docker 快装与源码部署两条线3.1 Docker 快速搭建路线如果只是搭一套测试环境先看效果Docker 是最快的路线。我当时的做法是三步走。第一步准备 MySQL 并初始化数据库。我是在同一台机器上已经有一个 MySQL 实例直接手动创建了一个名为cat的数据库CREATE DATABASE IF NOT EXISTS cat DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;初始化表结构不需要自己写 SQL 脚本CAT 服务端第一次启动时会自动完成建表。如果遇到权限受限无法建表那就要拿官方仓库里的初始化脚本手动执行这个后面再细说。第二步准备数据目录和配置文件。创建目录/data/appdatas/cat在里面放一个datasources.xml告诉服务端怎么连数据库。内容大致是这样的?xml version1.0 encodingutf-8? data-source data-source iddefault urljdbc:mysql://127.0.0.1:3306/cat/url usercat/user passwordyour_password/password /data-source /data-source注意这个配置文件的格式不同版本会有一点差异但总体的 XML 结构类似核心就是指定 JDBC URL、用户名、密码。我当时直接在这里把待办事项漏了账号的权限不够导致启动时连不上库控制台报 500。所以一定要在 MySQL 里先给这个账号赋上全库权限。第三步启动容器。我当时用的是社区维护的镜像命令大概长这样docker run -d --name cat-server \ -p 8080:8080 -p 2280:2280 \ -v /data/appdatas/cat:/data/appdatas/cat \ -e CAT_HOME/data/appdatas/cat \ registry.example.com/cat-server:3.0这里-v把宿主机的数据目录挂载进容器让配置文件和日志持久化容器重启也不丢。启动后看日志确认没有异常然后访问http://服务器IP:8080/cat能出现欢迎页就说明服务端起来了。3.2 源码编译部署路线生产环境我更推荐源码部署因为可以自己定制 JVM 参数、方便扩节点、也更容易排查问题。源码部署的流程也不复杂核心步骤是拉取源码、执行 Maven 构建、部署产物。先准备好构建环境JDK 1.8 加 Maven 3.x确认mvn -v能正常输出。然后从官方仓库克隆代码执行构建git clone 官方仓库地址 cat-src cd cat-src mvn clean install -DskipTests构建完成后在cat-home模块下会生成可部署的产物。我当时的做法是把cat-home的target目录里的 war 包直接丢到 Tomcat 的webapps下然后把web.xml里指定的数据目录建立好一样放/data/appdatas/cat并写入datasources.xml。启动 Tomcat等日志出现启动完成标志访问控制台验证。源码部署比 Docker 多出来的工作量主要是环境配置Tomcat 的启动参数需要设大一点尤其是堆内存。我一般给服务端分配至少 2G 的堆采集中等规模的团队是完全够用的。另外集群模式时我们需要确保每个节点都有独立的会话和路由这个一般通过配置管理工具分发配置来实现。3.3 启动验证与多节点扩展无论是 Docker 还是源码方式装完之后都建议按这个顺序快速验证登录控制台进入管理页面能看到当前服务端节点状态。在服务器本机用telnet 127.0.0.1 2280测试一下端口通不通。去客户端机器上再测一次telnet 服务器IP 2280保证跨机器能通。到这里服务端就绪了。如果以后流量大了需要扩成集群思路也不复杂再起几个同样的服务端节点共同压到一个 MySQL 上然后在客户端配置里把多个服务器 IP 都写上CAT 客户端会自动做负载均衡和故障转移。HDFS 只有在需要长期保留历史报表时才值得引入否则建议先不加把架构保持得简单一些。注意Docker 路线里容器一旦重建/data/appdatas/cat 如果没挂载会丢配置等于重新装一遍。生产环境强烈建议挂载宿主目录。4. Java 客户端接入从依赖到首条业务报表4.1 加依赖和写 client.xml服务端只是把收数据的仓库建好了真正的核心是把客户端接进来。CAT 的 Java 客户端是通过 cat-client 这个 SDK 被集成到业务应用里的。在 Maven 中央仓库搜cat-client把当前稳定版坐标加到pom.xml里即可。版本号一定要和服务端兼容我当时因为版本不匹配客户端日志反复报 JSON 解析异常后来统一版本后才消停。依赖加好之后还要在应用的src/main/resources目录下放一个客户端配置文件。CAT 客户端默认会去找client.xml内容需要指明服务端的地址?xml version1.0 encodingutf-8? config modeclient servers server ip192.168.1.100 port2280 / /servers /config如果以后服务端做了集群这里就继续加server节点。客户端本地上报数据失败时会先把日志写入本地缓存等网络恢复后再补报这在断网场景下能保留一些重要数据。不过补报机制依赖本地磁盘目录的写权限通常 CAT 客户端会向CAT_HOME目录写日志没有配置CAT_HOME的情况下默认写到用户目录的某个隐藏文件夹需要留意磁盘空间。4.2 三种最常用的埋点 API依赖和配置文件搞定后应用启动时就会自动上报 JVM 数据比如内存、线程数、Class 加载数量等这些不用写任何代码就能在控制台看到。但真正有价值的是自定义埋点三个高频 API 我先列出来给大家。Transaction 是核心用来记录一段完整操作的耗时和状态。比如我处理一个下单请求想看看整个方法花了多少时间就可以这样打点Transaction t Cat.newTransaction(URL, /order/create); try { // 你的业务逻辑 Thread.sleep(100); // 模拟耗时操作 t.setSuccessStatus(); } catch (Exception e) { Cat.logError(e); t.setStatus(e); } finally { t.complete(); }newTransaction有两个参数第一个是类型第二个是名称我习惯按URL 和具体路径来命名这样在报表里能很清楚看到每个请求链路。注意complete()一定要在 finally 里调用否则 Transaction 不会结束报表这个节点会一直挂在那里。Event 用来记录一次事件比如 某个缓存失败了、某个开关被打开了它关注的是发生了没有而不是耗时Cat.logEvent(Cache, Redis.getFailed);Metric 用来做计数器比如每分钟成功下单量Cat.logMetricForCount(order.create.success);还有logMetricForDuration这类 API可以记录某个操作的耗时平均值适合做业务侧的 SLA 监控。总之一句话Transaction 负责耗时和状态Event 负责事件发生Metric 负责计数与数值统计三者组合起来基本覆盖了所有监控诉求。4.3 验证首条数据是否上报成功接完客户端启动应用等大概一到两分钟回到控制台找到实时报表或客户端状态如果配置正确很快就能看到这个应用的主机名和 IP 出现在列表里。我习惯的做法是直接在代码里打一个 Test Transaction然后去报表里搜这个名称能搜到就说明链路全通了。这里有个容易被忽略的点首次部署时服务端可能还没有生成路由表客户端向服务端上报了数据但控制台要等下一轮路由刷新才能看到。看到没数据别急着重启服务等两三分钟再看一般就会出现了。这个我在下一章也会提到。5. 踩过的六个坑逐条复盘5.1 首屏一直空白问题出在数据目录权限我第一次部署时控制台登录成功页面也能打开但所有报表区域都是空白等了十分钟还是空白。后来去翻服务端日志发现大量目录创建失败相关的报错根源是/data/appdatas/cat目录的所有者是 root而服务端进程是以普通用户启动的。解决办法非常原始chown -R 用户:用户组 /data/appdatas/cat再重启服务端就好。这个坑之所以要特别强调是因为页面没有任何明显的报错提示新手很容易在日志里迷路。5.2 客户端日志提示无法连接服务端但其实不是网络问题有一次客户端日志已经明确报connect refused了但我测试端口是通的抓破脑袋想不通。后来发现客户端配置里的 IP 写成了服务端的内网 IP而客户端和服务端之间经过了一组防火墙规则只放行了某个固定 IP。说白了还是配置里的 IP 不匹配。建议所有配置统一走内网域名不要直接写 IP后续扩容换机器时也方便。如果非得上报源 IP一定要把网络路径提前画清楚。5.3 服务器时间不同步报表全部乱套CAT 的数据都按小时和分钟聚合客户端上报时会带上时间戳服务端按这个时间戳归类。如果某台业务服务器的系统时间快了五分钟那么这五分钟内的数据全部会被归到前一个时间段而且里面告警规则经常会因为时间不同步产生误报。解决办法就是在所有机器上部署好 NTP 校时并且在安装清单里把它当作必检项。5.4 MySQL 连接池配置问题导致服务端频繁告警CAT 服务端默认会对 MySQL 连接数做限制而服务端在高峰期会并发写入大量报表数据如果数据库连接数配额配小了会频繁报 can not get connection。这个问题的解决方式是在datasources.xml里把最大连接数调大并同时调大 MySQL 侧的最大连接数限制。注意不是只在一边调两边的值都要匹配。5.5 报表数据保留时间不够先别急着上 HDFSCAT 默认的历史报表数据是存在本地文件并依赖 HDFS 做长期归档。如果你的诉求只是保留最近一周的数据用于日常排查那可以先不上 HDFS。但我踩过的坑是默认的本地归档清理逻辑如果不摸清磁盘会被报表文件塞满。建议在服务端配置里明确报表文件的保留策略比如定期清理超过 3 天的本地文件磁盘告警提前配置好别等服务器写满再处理。5.6 日志里一大堆 JSON 解析错误版本不匹配客户端 3.0 以上的版本和服务端 3.0 以下的版本混用会有序列化协议上的问题。最常见的表现是客户端上报数据时报 JSON 解析异常但网络和配置看起来完全正常。排查到这一步一定要去看客户端实际使用的 cat-client 版本和生产服务端版本保持一致。版本管理这一环建议纳入发布流水线避免上线时节点间的 SDK 版本不一致。把上面这些坑汇总成一张表方便收藏问题具象表现我的处理方式数据目录权限不足控制台空白日志大量目录报错chown 给到应用启动用户IP 配置错误客户端连不上服务端改用内网域名配置时间不同步报表时间错乱告警误报全节点 NTP 校时MySQL 连接池不足服务端频繁报数据库连接异常同步调大客户端与服务端连接池本地文件无限增长磁盘爆满配置清理策略提前告警SDK 版本不一致上报数据解析异常统一 cat-client 版本6. 一次真实排障链路CAT 是怎么帮我省下三个小时的6.1 场景描述模拟项目 X 的支付回调接口变慢某天下午支付回调接口的耗时毛刺变得很明显。从全局看P99 从 300 毫秒升到 1.5 秒但数据库、缓存、消息队列都没有明显压力链路也不知道卡在哪。这种场景在没有监控的时候是最痛苦的因为系统所有组件都看似正常问题藏在调用栈深处。6.2 定位过程从 Transaction 树形下钻打开 CAT 控制台在 Transaction 报表里找到支付回调接口点进去可以看到这通调用的完整链路树。首先看到总耗时 1.5 秒子节点下钻下去约 800 毫秒消耗在一个内部服务 A 的 HTTP 调用上。点进 A 服务继续下钻又看到 750 毫秒消耗在某个连接池获取连接上。继续点进去发现是这个连接池配置的最大等待时间不合理某个线程把连接占用的时间又特别长后面的请求全在排队。整个定位过程大概 20 分钟。如果没有 CAT我得先在负载均衡层、网络层、每个服务日志里来回翻保守估计三个小时起。这也验证了选型时的判断复杂的业务链路不能只靠全局性能指标还要有能逐层下钻的树形链路而 CAT 的 Transaction 就是为这个场景设计的。6.3 事后复盘的三个改善动作这次排障给我们团队带来的改善不止是修了一个参数。第一我们给所有外部 HTTP 调用统一加了 CAT 埋点让每个调用的等待时间、耗时、成功率都能在报表里看到第二把连接池获取连接的耗时也做了监控超过阈值立刻告警第三把这次排查经验沉淀为团队文档遇到类似问题先看 Transaction 树形结构的耗时占比分布优先钻取占比最高的子节点。7. 一些私人经验谈用了一段时间之后最大的体会是CAT 的安装只是一阵子的事埋点习惯才是一辈子的事。服务端搭起来之后真正持续维护的其实是哪些 URL 值得打点、哪些异常必须告警、哪些指标需要长期跟踪这类规则。不要想着一次把全链路埋完先挑核心链路比如登录、下单、支付、回调跑通之后再逐步铺开。最后分享一个小技巧把统一埋点封装进自己的 MVC 拦截器或者 Spring AOP 切面里所有接口自动生成 Transaction埋点代码不用散落在业务逻辑里。这样新同事接手时不需要理解 CAT API照样能获得完整链路数据。等到哪天接口慢了打开控制台看到一个清晰的耗时树你会感谢当初认真接 CT 的自己。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。