简介:CAT实时应用监控平台 v3.1.0 源码包面向IT运维人员、Java开发者及计算机专业学生,用于实时监测应用性能与稳定性,可支撑毕业设计论文撰写与分布式监控案例研究。压缩包共2000个文件,约29.13MB,以1108个Java源文件与481个XML配置为主,辅以JS前端脚本、C/C++底层实现、Python与Go辅助模块,以及Markdown说明、YAML与JSON配置等,覆盖系统架构、模块划分、数据处理与异常处理机制。已有113人学习下载。通过研读源码可掌握监控指标采集、日志聚合、报警通知与数据可视化等关键实现,配合说明文档快速完成部署与配置,是理解分布式系统监控技术细节的实操素材。
1. CAT实时应用监控平台 v3.1.0:从告警延迟到秒级定位的落地路径
凌晨两点,订单服务突然大量超时,运维群里炸了锅。你手里只有 Zabbix 的 CPU 曲线和几台机器的top输出,根本不知道是哪个接口拖垮了整条链路。这种场景下,CAT(Central Application Tracking)实时应用监控平台就是用来终结这种“盲人摸象”式排查的。它是一套开源的分布式实时监控系统,核心能力是把一次请求在多个服务间的调用链路、耗时、异常、SQL 执行、缓存访问全部串起来,做到秒级聚合与分钟级定位。v3.1.0 这个版本号意味着你拿到的是一份可独立部署的发布包,而不是需要自己从源码编译的半成品。这篇文章面向的是正在选型或准备落地 CAT 的后端工程师、SRE 和运维负责人,目标很明确:让你搞清楚 CAT 能解决什么问题、怎么把它跑起来、消息树和报表怎么配、以及哪些坑会让你的监控变成“玄学”。
2. CAT 的消息模型与实时链路:为什么它比日志 grep 快一个量级
2.1 消息树:一次请求如何被拆成可聚合的原子事件
CAT 的核心抽象叫“消息树”(Message Tree)。一次用户请求进入系统后,CAT 客户端会生成一个根 Transaction,后续每一次 RPC 调用、数据库查询、缓存读写、本地方法执行,都会作为子节点挂到这棵树上。每个节点记录类型、名称、状态、耗时、时间戳和附加数据。这跟传统日志的区别在于:日志是扁平的文本流,而消息树是结构化的调用关系图。
为什么这个设计能支撑实时监控?因为 CAT 客户端在本地就把消息按分钟窗口做了预聚合。比如一个 Transaction 类型为URL、名称为/api/order/create的节点,客户端不会把每一条原始消息都发到服务端,而是先在内存里统计这一分钟内该名称的调用次数、失败次数、平均耗时、95 线、99 线,然后只把聚合结果上报。服务端收到的是已经压缩过的指标,而不是海量原始日志。这就是 CAT 能做到“实时”的底层原因——它把计算下推到了客户端。
常见做法是:在 Java 应用里引入cat-client依赖,通过Cat.logTransaction()或 AOP 自动埋点。对于 Spring Boot 项目,通常用cat-spring-boot-starter做自动装配。消息树的结构决定了你后续在 CAT 服务端能看到什么粒度的报表。如果埋点只埋了 URL,那你就只能看到接口级别的耗时;如果同时埋了 SQL 和 Cache,才能在下钻时看到具体是哪条 SQL 拖慢了接口。
2.2 客户端聚合与服务端消费:数据从产生到可查的完整路径
CAT 的数据流分三段:客户端采集与聚合、服务端接收与存储、报表查询与告警。
客户端侧,每个线程维护一个上下文栈。当Transaction开始时压栈,结束时出栈并计算耗时。子消息(Event、Metric、Heartbeat)在产生时直接挂到当前栈顶的 Transaction 下。每分钟结束时,客户端把这一分钟内的消息树按类型和名称做聚合,生成MessageAggregator,然后通过长连接或 HTTP 批量发送到服务端。这里有个关键参数:cat.client.maxQueueSize,默认 5000,如果客户端产生消息的速度超过发送速度,队列会堆积,超过阈值后新消息会被丢弃。生产环境建议根据 QPS 调整到 10000 以上。
服务端侧,CAT 的cat-consumer模块负责接收消息,写入本地文件系统(不是数据库),再由cat-home模块读取文件生成报表。这种“文件存储 + 内存索引”的架构让 CAT 在写入时几乎没有瓶颈,代价是历史数据查询依赖文件保留策略。v3.1.0 的默认保留周期是 7 天,可以通过cat.consumer.log.queue.size和cat.consumer.log.save.hour调整。
提示:CAT 服务端不依赖 MySQL 或 Elasticsearch 做实时存储,这是它跟很多 APM 产品最大的架构差异。好处是部署简单、写入快;坏处是跨天查询和全文检索能力弱,需要配合离线归档。
2.3 最小可运行部署:单机模式跑通第一个监控报表
下面以 Linux 环境为例,把 CAT v3.1.0 的发布包跑起来。假设你已经拿到了CAT实时应用监控平台 v3.1.0.zip,解压后得到cat-home、cat-consumer、cat-client等目录。
第一步,准备环境。CAT 服务端依赖 JDK 8 和 Tomcat 7/8,不建议用 JDK 11 以上,会有反射相关的兼容问题。
# 检查 Java 版本,必须是 1.8 java -version # 解压发布包 unzip CAT实时应用监控平台\ v3.1.0.zip -d /opt/cat # 进入 cat-home 目录,这是 Web 控制台 cd /opt/cat/cat-home第二步,配置数据源。CAT 服务端自身需要一个 MySQL 来存储配置信息和用户权限,实时监控数据仍然走文件。修改src/main/resources/application.properties或解压后的WEB-INF/classes/application.properties:
# CAT 自身元数据库,不是监控数据存储 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/cat?useUnicode=true&characterEncoding=utf8 spring.datasource.username=cat spring.datasource.password=cat123 # 服务端接收端口,客户端上报走这个端口 cat.server.port=2280 # 报表数据文件存储路径 cat.consumer.log.path=/data/applogs/cat第三步,初始化数据库。在 MySQL 中创建cat库,然后执行发布包中script/Cat.sql建表脚本。这个脚本会创建project、user、config等表。
第四步,启动服务端。CAT 的cat-home是一个标准 WAR 包,可以丢进 Tomcat 的webapps目录,也可以用内置的启动脚本。
# 方式一:用 Tomcat 启动 cp cat-home.war $TOMCAT_HOME/webapps/ $TOMCAT_HOME/bin/startup.sh # 方式二:用发布包自带的脚本(如果有) cd /opt/cat/cat-home sh startup.sh启动后访问http://127.0.0.1:8080/cat,默认账号admin,密码admin。登录后进入“项目列表”,新建一个项目,项目名填你的应用名,比如order-service。这个项目名必须和客户端配置里的cat.project.name一致,否则报表里看不到数据。
第五步,客户端接入。在 Java 应用的启动参数里加上 CAT 客户端配置:
# JVM 启动参数 -Dcat.project.name=order-service -Dcat.home=/opt/cat/cat-client -Dcat.server.address=127.0.0.1:2280然后在代码里手动埋一个 Transaction:
// 手动埋点示例:记录一次订单创建请求 Transaction t = Cat.newTransaction("URL", "/api/order/create"); try { // 业务逻辑 orderService.create(order); t.setStatus(Transaction.SUCCESS); } catch (Exception e) { t.setStatus(e); Cat.logError(e); // 记录异常堆栈 } finally { t.complete(); // 必须调用,否则消息不会上报 }Cat.newTransaction的第一个参数是类型,常用类型有URL、SQL、Cache、RPC、Service;第二个参数是名称,建议用接口路径或方法名。t.complete()必须放在 finally 里,否则这条消息会一直挂在栈上,导致后续消息树结构错乱。Cat.logError(e)会把异常堆栈作为 Event 挂到当前 Transaction 下,在报表的“异常”页可以看到。
跑通之后,在 CAT 控制台的“Transaction”报表里应该能看到/api/order/create的调用次数和耗时曲线。如果看不到,先检查客户端和服务端时间是否同步,再检查cat.server.address是否可达。
3. 报表配置与告警规则:让监控从“能看”变成“能报警”
3.1 核心报表类型与下钻路径
CAT 的报表分几大类:Transaction 报表、Event 报表、Problem 报表、Heartbeat 报表、Business 报表。每类报表解决不同问题。
Transaction 报表展示的是调用量和耗时分布。进入某个 Transaction 名称后,可以看到它的“调用来源”和“调用去向”,这是排查链路问题的关键。比如/api/order/create耗时升高,下钻后看到它调用了SQL: select * from order,而这条 SQL 的耗时占了 80%,问题就定位到了数据库层。
Event 报表记录的是异常和自定义事件。Cat.logError(e)产生的异常会出现在这里,按异常类型聚合。如果某个异常突然暴涨,Event 报表会第一时间反映出来。
Problem 报表是 CAT 的“智能告警”模块。它根据你配置的规则,自动检测异常情况,比如“某 Transaction 的失败率超过 5%”“某 SQL 的平均耗时超过 200ms”。Problem 报表不需要你写复杂的告警表达式,只需要在“告警配置”里设定阈值和通知方式。
Heartbeat 报表用于监控应用实例的存活状态。客户端每分钟上报一次心跳,如果某个实例超过 3 分钟没有心跳,会在 Heartbeat 报表里标记为“失联”。
注意:CAT 的报表是按分钟聚合的,所以告警有 1 分钟左右的延迟。如果你需要秒级告警,CAT 本身不擅长,需要配合其他工具。
3.2 告警规则配置:从阈值设定到通知渠道
CAT 的告警配置在“配置”菜单下的“告警配置”里。每条规则包含几个要素:项目名、报表类型、监控项、阈值、通知方式。
以“订单创建接口失败率超过 5%”为例,配置步骤如下:
第一步,选择项目order-service,报表类型选Transaction,监控项选URL:/api/order/create。
第二步,设定规则。CAT 支持几种规则类型:FailureRate(失败率)、AvgTime(平均耗时)、Count(调用量)、MaxTime(最大耗时)。这里选FailureRate,阈值填5,单位是百分比。
第三步,设定通知方式。CAT 支持邮件、短信、HTTP 回调。邮件需要在application.properties里配置 SMTP;HTTP 回调需要填一个接收告警的 URL,CAT 会以 POST 方式发送 JSON 格式的告警内容。
# 邮件告警配置 cat.alert.mail.host=smtp.example.com cat.alert.mail.port=465 cat.alert.mail.username=alert@example.com cat.alert.mail.password=xxxxxx cat.alert.mail.receivers=oncall@example.com第四步,保存并启用。规则生效后,如果/api/order/create在某一分钟的失败率超过 5%,CAT 会生成一条 Problem 记录,并按配置的通知方式发送告警。
这里有个容易翻车的地方:CAT 的失败率计算是基于客户端上报的Transaction.SUCCESS和异常状态。如果你在代码里 catch 了异常但没有调用t.setStatus(e),CAT 会认为这次调用是成功的,失败率永远是 0。所以埋点时一定要确保异常状态被正确设置。
3.3 业务监控指标:用 Metric 上报自定义数据
除了自动埋点的 Transaction 和 Event,CAT 还支持自定义 Metric。比如你想监控“订单创建数量”这个业务指标,可以用Cat.logMetricForCount:
// 每创建一笔订单,计数加一 Cat.logMetricForCount("order_create_count"); // 记录订单金额的分布 Cat.logMetricForDuration("order_create_amount", order.getAmount());logMetricForCount适合计数型指标,logMetricForDuration适合耗时或金额等连续值。这些指标会出现在 Business 报表里,可以按分钟、小时、天查看趋势。
Metric 的上报频率是每分钟一次,客户端会在本地累加,然后一次性上报。所以如果你在一分钟内调用了 1000 次logMetricForCount,服务端收到的是“这一分钟 order_create_count = 1000”,而不是 1000 条独立消息。这个设计对性能很友好,但意味着你无法用 Metric 做去重或明细查询。
4. 避坑与排查:CAT 落地时最容易翻车的 5 个场景
4.1 客户端消息丢失:队列满了但没人告诉你
现象:CAT 报表里数据断断续续,某些分钟完全没有数据,但应用日志正常。
原因:客户端消息发送队列满了。CAT 客户端默认队列大小是 5000,如果服务端接收慢或网络抖动,队列会堆积。超过阈值后,新消息被直接丢弃,而且默认不打印日志。
解决:调大cat.client.maxQueueSize,并在客户端配置里开启丢弃日志。在CatClient初始化时设置:
// 调大队列并开启丢弃告警 System.setProperty("cat.client.maxQueueSize", "20000"); System.setProperty("cat.client.log.discard", "true");同时检查服务端cat-consumer的消费速度,如果服务端处理不过来,需要扩容 consumer 实例或调大cat.consumer.log.queue.size。
4.2 消息树断链:异步线程里 Transaction 丢失
现象:在异步线程或线程池里执行的逻辑,在 CAT 报表里看不到,或者挂在了错误的父节点下。
原因:CAT 的上下文是基于 ThreadLocal 的,跨线程时上下文不会自动传递。如果在主线程创建 Transaction,然后在子线程里执行 SQL,子线程里Cat.getCurrentMessage()拿到的是 null。
解决:使用Cat.getManager().getThreadLocalContext()手动传递上下文,或者用 CAT 提供的CatContext包装 Runnable:
// 在主线程获取上下文 Context ctx = Cat.getManager().getThreadLocalContext(); // 在子线程中设置上下文 executor.submit(() -> { Cat.getManager().setThreadLocalContext(ctx); try { // 异步逻辑,埋点会挂到正确的父节点下 Transaction t = Cat.newTransaction("Service", "asyncTask"); // ... t.complete(); } finally { Cat.getManager().removeThreadLocalContext(); } });如果用的是 Spring 的@Async,需要自定义AsyncConfigurer来传递 CAT 上下文,否则异步方法里的埋点会变成“孤儿消息”。
4.3 服务端时间不同步导致报表错位
现象:报表里的数据时间戳和实际时间差了几个小时,或者某些分钟的数据跑到了其他分钟里。
原因:客户端和服务端时间不同步。CAT 的消息树里记录的是客户端时间戳,服务端按这个时间戳做分钟窗口聚合。如果客户端时间比服务端快 5 分钟,那客户端 10:00 产生的消息会被服务端归到 10:05 的窗口里。
解决:所有部署 CAT 客户端的机器必须开启 NTP 时间同步。在 Linux 上检查:
# 查看时间同步状态 timedatectl status # 如果 NTP 未启用,安装并启动 yum install -y ntp systemctl start ntpd systemctl enable ntpd服务端和客户端的时间偏差控制在 1 秒以内,否则报表会出现“数据漂移”。
4.4 磁盘写满:CAT 的日志文件不会自动清理
现象:CAT 服务端运行一段时间后,磁盘使用率飙升,最终写满导致服务不可用。
原因:CAT 的实时数据以文件形式存储在cat.consumer.log.path下,默认保留 7 天,但如果你调整过保留策略或者文件句柄没释放,旧文件可能不会被删除。
解决:定期检查存储目录,配置定时清理任务。CAT 自身有清理线程,但依赖cat.consumer.log.save.hour参数。建议在 crontab 里加一条兜底清理:
# 每天凌晨 3 点清理 7 天前的 CAT 日志 0 3 * * * find /data/applogs/cat -name "*.log" -mtime +7 -delete同时监控磁盘使用率,超过 80% 就告警。CAT 的日志文件增长速度和你的消息量成正比,高 QPS 场景下一天可能产生几十 GB 文件,务必提前规划存储。
4.5 报表查询超时:大项目下的索引膨胀
现象:在 CAT 控制台查询某个 Transaction 的历史数据时,页面加载超过 30 秒甚至超时。
原因:CAT 的报表查询依赖内存索引,如果项目下 Transaction 名称过多(比如超过 5000 个),索引会膨胀,查询时需要遍历大量文件。
解决:控制 Transaction 名称的粒度。不要用动态参数作为名称,比如/api/order/{orderId}应该写成/api/order/detail,否则每个 orderId 都会生成一个独立的 Transaction 名称,索引爆炸。在代码里做名称归一化:
// 错误做法:名称里带动态 ID Transaction t = Cat.newTransaction("URL", "/api/order/" + orderId); // 正确做法:名称归一化 Transaction t = Cat.newTransaction("URL", "/api/order/detail"); t.addData("orderId", orderId); // 动态数据放在 addData 里addData的内容不会参与索引,只作为附加信息存储,查询时可以在消息详情里看到。这样既保留了排查所需的上下文,又不会让索引膨胀。
5. 用 CAT 的 Transaction 下钻做一次真实故障复盘
前面讲了怎么部署、怎么配告警、怎么避坑。最后用一个我实际处理过的故障场景,把 CAT 的下钻能力串起来。这个技巧比任何配置都值钱:从 Problem 报表反查 Transaction,再从 Transaction 下钻到 SQL 和 RPC,最后用 Event 报表确认异常堆栈。
故障现象:某天下午 3 点,订单服务的创建接口 P99 从 200ms 飙升到 3 秒,但 CPU 和内存都正常。
第一步,打开 CAT 的 Problem 报表,看到一条告警:“order-service 的 URL:/api/order/create 平均耗时超过 1000ms”。点击这条 Problem,直接跳转到对应的 Transaction 报表。
第二步,在 Transaction 报表里,把时间范围锁定在故障时间段,看到/api/order/create的耗时曲线在 3:05 开始抬升。点击“调用去向”标签,看到它调用的SQL: select * from order where user_id = ?耗时从 10ms 涨到了 2.8 秒。
第三步,点击这条 SQL 的名称,进入 SQL 报表。这里可以看到这条 SQL 的调用来源是order-service,并且有一个“慢 SQL 采样”列表。点开一条采样记录,看到完整的 SQL 语句和执行计划。发现user_id字段上没有索引,而当天下午订单表新增了 200 万行数据,全表扫描导致耗时暴涨。
第四步,回到 Event 报表,确认没有异常堆栈,说明不是代码 bug,纯粹是数据库性能问题。在user_id上加索引后,耗时回落到 15ms。
整个排查过程不到 5 分钟。如果没有 CAT,你可能需要登录每一台机器 grep 日志,再手动拼凑调用关系,半小时都未必能定位到 SQL 层。
这里有个习惯我一直在用:每次上线新接口,先看 CAT 的 Transaction 报表里有没有出现“未知名称”的 Transaction。如果有,说明埋点名称没归一化,或者有未预期的调用路径。这个习惯帮我提前发现过好几次“隐藏的慢 SQL”和“意外的远程调用”。CAT 的价值不在于它有多少报表,而在于你能不能顺着消息树一路下钻到根因。希望帮到你。
本文还有配套的精品资源,点击获取