- 可观测性
- 指标监控
- 告警
- APM
- 后端
- 链路追踪
【免费下载链接】cat
CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
本指南围绕 CAT 项目提供的springMVC-AOP集成方案展开,讲解如何以切面注解方式快速对系统进行埋点:只需在需要监控的类或方法上添加注解,即可自动生成 CAT Transaction 上报,同时配合 Filter 完成 URL 请求级埋点与动态 URL 聚合。读完本文,你将掌握CatTransaction、CatCacheTransaction、CatHttpRequestTransaction、CatDubboClientTransaction四类注解的完整用法、底层切面实现原理,以及配套的 MyBatis SQL 埋点与切面装配方式,可直接落地到基于 Spring/SpringMVC 的业务项目中。
一、方案概述:为什么用切面注解埋点
CAT(大众点评开源的应用监控平台)要求业务代码通过Cat.newTransaction/Cat.logEvent等 API 主动上报数据。如果每个方法都手写「开启 Transaction → 执行业务 → 设置状态 → complete」,会带来大量重复样板代码,且容易在异常分支遗漏状态设置。
springMVC-AOP集成方案的做法是:把「开启、收尾、记录状态」的共性逻辑统一收敛进切面(CatAspect.java),业务侧只负责在方法上加一个注解,由 AspectJ 在运行时拦截并完成埋点。该方案在仓库的 integration/README.md 中被归入「埋点方案」体系,与 log4j2、logback、spring-boot、mybatis 等集成并列。
需要明确的是,注解方式会引入 AOP 代理与反射开销,原文档也明确提示会有性能影响,因此适用于对响应时间不敏感的内部方法(如缓存读写、Dubbo 客户端调用、URL 聚合),而不是每一条高频热点路径。
二、前置准备:引入依赖并配置 CatFilter
1. 添加 cat-client 依赖
在pom.xml中引入 CAT 客户端:
<dependency> <groupId>com.dianping.cat</groupId> <artifactId>cat-client</artifactId> <version>2.0.0</version> </dependency>版本号以你实际接入的 CAT 服务端版本为准,这里仅为原文档示例(CAT部署.txt)。
2. 在 web.xml 中配置 CatFilter
原文档给出的第一步是在web.xml中添加 Filter,url-pattern配置为需要埋点的 restful 接口,其目的是让请求级埋点只作用于业务接口、防止静态资源乱入:
<filter> <filter-name>cat-filter</filter-name> <filter-class>com.dianping.cat.servlet.CatFilter</filter-class> </filter> <filter-mapping> <filter-name>cat-filter</filter-name> <url-pattern>/test1/</url-pattern> <url-pattern>/test2/</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping>要点说明:
- url-pattern:按需写业务接口前缀(如
/api/、/orders/),避免/*全量匹配导致图片、CSS、JS 等静态资源也进入埋点链路。 - dispatcher:同时配置
REQUEST与FORWARD,保证直接请求与转发后的请求都能被拦截上报。 - 从当前仓库源码看,Filter 的实际实现类位于 cat-client/src/main/java/com/dianping/cat/support/servlet/CatFilter.java,它承担了 URL 请求级埋点的大部分工作——即原文档所说「该 filter 已经对 URL 请求做了大部分的埋点工作」。若你的 cat-client 版本类路径不同,请以实际 jar 内类名为准。
3. 客户端全局配置
Filter 与切面最终都依赖 cat-client 上报链路,因此还需保证客户端配置就绪:在src/main/resources/META-INF/下新建app.properties写入app.name=你的应用名,并把client.xml(含 CAT 服务端ip/port列表)放置到/data/appdatas/cat/目录下(详见 CAT部署.txt 中的客户端配置章节)。
三、通用方法埋点注解 CatTransaction
在切面注解体系中,CatTransaction是最基础的一枚注解,适用于任意需要以 Transaction 维度统计的方法。
注解定义
CatTransaction.java 定义了两个属性:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CatTransaction { String type() default "Handler"; // "URL MVC Service SQL" is reserved for Cat Transaction Type String name() default ""; }type:Transaction 类型,默认Handler。源码注释特别提醒:URL、MVC、Service、SQL是 CAT 预留的 Transaction 类型,自定义时不要占用这些保留字,以免与 CAT 自身埋点类型冲突。name:Transaction 名称,为空时由切面根据方法签名自动生成。
切面实现原理
对应切面方法为catTransactionProcess(CatAspect.java):
@Around("@annotation(catTransaction)") public Object catTransactionProcess(ProceedingJoinPoint pjp, CatTransaction catTransaction) throws Throwable { String transName = pjp.getSignature().getDeclaringType().getSimpleName() + "." + pjp.getSignature().getName(); if (StringUtils.isNotBlank(catTransaction.name())) { transName = catTransaction.name(); } Transaction t = Cat.newTransaction(catTransaction.type(), transName); try { Object result = pjp.proceed(); t.setStatus(Transaction.SUCCESS); return result; } catch (Throwable e) { t.setStatus(e); throw e; } finally { t.complete(); } }关键设计点:
- 默认命名规则:不写
name时,Transaction 名为简单类名.方法名(如OrderService.getOrders),天然具备可读性;显式指定name则覆盖默认值,适合做跨方法聚合。 - 异常语义:方法抛异常时调用
t.setStatus(e)记录异常信息并重新抛出,不吞异常、不改变业务行为;正常路径设置SUCCESS。 - finally 兜底:无论成功失败都在
finally中complete(),保证 Transaction 一定被闭合、不会泄漏。
使用示例
@CatTransaction // 默认 type=Handler,name=简单类名.方法名 public void processOrder(Long orderId) { // 业务逻辑 } @CatTransaction(type = "Service", name = "OrderCreate") public void createOrder(Order order) { // 业务逻辑 }四、缓存埋点注解 CatCacheTransaction
针对缓存读写场景(典型如 Redis 封装),CatCacheTransaction把每次缓存操作封装成一个Cache.Redis类型的事务,并额外上报缓存服务器事件。
注解定义
CatCacheTransaction.java:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CatCacheTransaction { String name() default ""; String server() default "Default"; }name:事务名,默认取被拦截方法名(如get、put、delete)。server:缓存服务器标识,默认Default,用于区分不同缓存实例。
切面实现与上报结构
对应切面方法catCacheTransactionProcess:
Transaction t = Cat.newTransaction("Cache.Redis", transName); try { Cat.logEvent("Cache.Server", catCacheTransaction.server()); Object result = pjp.proceed(); t.setStatus(Transaction.SUCCESS); return result; } catch (Throwable e) { Cat.logEvent("Cache.Server", catCacheTransaction.server(), "-1", null); t.setStatus(e); throw e; } finally { t.complete(); }上报的数据结构为:
| 上报项 | 类型 | 值 |
|---|---|---|
| Transaction | type=Cache.Redis | name=方法名或注解 name |
| Event | type=Cache.Server | 成功时值为server()属性,失败时值为server()+ 状态码-1 |
成功/失败两条分支都会记录Cache.Server事件,但失败分支额外携带状态码-1,便于在 CAT 报表中按「缓存服务器 + 是否失败」快速筛选定位。
使用示例(原文档示例)
@CatCacheTransaction public V get(K key) { // 缓存读取 } @CatCacheTransaction public void put(K key, V value) { // 缓存写入 } @CatCacheTransaction public void delete(K key) { // 缓存删除 } // 显式指定缓存服务器 @CatCacheTransaction(name = "OrderCache.get", server = "redis-order-01") public Order getOrder(Long orderId) { // ... }五、URL 聚合注解 CatHttpRequestTransaction
背景:为什么要做 URL 聚合
SpringMVC 中带路径参数的接口(如/orders/{userId}/{orderStatus})如果按原始 URL 埋点,每个用户 ID、订单状态组合都会生成独立的事务名,导致 CAT 报表出现海量 URL 分散项,既浪费服务端存储、又难以看清接口整体趋势。切面方案通过@After在响应前改写请求属性cat-page-uri,把动态 URL 归一到固定业务名,从而降低 CAT 服务压力(原文档原话)。
注解定义
CatHttpRequestTransaction定义如下(与 CatAspect.java 配套,原文档在 CAT部署.txt 中给出了完整定义):
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CatHttpRequestTransaction { String name() default ""; String type() default "URL"; }切面实现
对应切面方法catHttpRequestProcess是@After通知,不做事务包裹,只改写请求属性:
@After("@annotation(catHttpRequestTransaction)") public void catHttpRequestProcess(CatHttpRequestTransaction catHttpRequestTransaction) { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); if (StringUtils.isNotBlank(catHttpRequestTransaction.name())) { String transName = catHttpRequestTransaction.name(); request.setAttribute("cat-page-uri", transName); } }工作原理:Filter 端读取cat-page-uri请求属性作为 URL 事务名;切面在 Controller 方法返回后(@After,不影响方法返回值)把注解上的聚合名写入该属性。因此注解的name必须与 Filter 的 url-pattern 命中范围配合使用,才能对同前缀的接口做归并。
使用示例(原文档示例)
@RequestMapping(value = "/orders/{userId}/{orderStatus}") @ResponseBody @CatHttpRequestTransaction(type = "URL", name = "/orders") public String userOrders() { // 无论 userId/orderStatus 如何变化,CAT 报表中 URL 统一显示为 /orders }实践中type使用默认值URL即可;需要区分来源时也可自定义(原文档给出的注解type()默认即为URL)。注意:只有name非空时切面才会改写cat-page-uri,留空则保持 Filter 的默认行为。
六、Dubbo 客户端调用埋点注解 CatDubboClientTransaction
对发起 Dubbo 调用的客户端方法,CatDubboClientTransaction会生成Call类型事务,并以事件形式记录被调应用与服务,用于跨服务调用链分析。
注解定义
CatDubboClientTransaction.java:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface CatDubboClientTransaction { String name() default ""; String callServer(); String callApp(); }注意与前面注解的差异:callServer()与callApp()是必填属性(无 default),编译期强制要求填写被调服务与被调应用。
切面实现与上报结构
对应切面方法:
Transaction t = Cat.newTransaction("Call", transName); try { Cat.logEvent("Call.app", catDubboClientTransaction.callApp()); Cat.logEvent("Call.server", catDubboClientTransaction.callServer()); Object result = pjp.proceed(); t.setStatus(Transaction.SUCCESS); return result; } catch (Throwable e) { t.setStatus(e); throw e; } finally { t.complete(); }上报的数据结构为:
| 上报项 | 类型 | 值 |
|---|---|---|
| Transaction | type=Call | name=方法名或注解 name |
| Event | type=Call.app | callApp()(被调应用) |
| Event | type=Call.server | callServer()(被调服务) |
这三个维度组合后,可在 CAT 中分析「本应用 → 某应用 → 某服务」的调用量、耗时与错误率。
使用示例(原文档示例)
@CatDubboClientTransaction(callApp = "orders", callServer = "orderServer") public List<Long> getOrdersByUser() { // 内部发起 Dubbo 调用 }七、配套 SQL 埋点:CatMybatisInterceptor
切面注解体系之外,springMVC-AOP目录还附带了一份 MyBatis SQL 埋点的完整实现 CatMybatisInterceptor.java,用于补全「SQL」这一 CAT 预留事务类型。
拦截器实现要点
该拦截器通过 MyBatis@Intercepts注解拦截Executor的update与query两个方法:
@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }), @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) public class CatMybatisInterceptor implements Interceptor {核心逻辑与切面如出一辙:以MappedStatement.getId()(即 Mapper 方法的全限定名)作为事务名,创建CatConstants.TYPE_SQL类型事务,同时上报SQL.Database(数据源 URL,通过 properties 注入)与SQL.Method(update/query)两个事件;异常时Cat.logError(e)记录堆栈。此外还提供了showSql工具方法,可将BoundSql中的?占位符替换为真实参数值,便于生成可执行 SQL 日志。
接入方式(原文档示例)
在mybatis-config.xml中注册插件,并通过 property 传入数据源 URL:
<!--mybatis + cat interceptor --> <plugin interceptor="com.test.center.util.aspect.CatMybatisInterceptor"> <property name="datasourceUrl" value="${center.jdbc.url}"/> </plugin>八、切面装配:让注解生效
注解本身不产生任何拦截,必须把CatAspect注册为 Spring 管理的 Aspect 才能生效。仓库以纯 Java 类提供切面(CatAspect标注了@Aspect但未标注@Component),实际项目中可用以下任一方式装配:
方式一:Spring XML 配置
<bean id="catAspect" class="com.yourcompany.aop.CatAspect"/> <aop:aspectj-autoproxy/>方式二:Spring Boot / Java Config
@Configuration @EnableAspectJAutoProxy public class CatAopConfig { @Bean public CatAspect catAspect() { return new CatAspect(); } }两点实践提醒:
- 切面生效依赖 Spring AOP 代理,因此不能直接监控同类内部方法调用(
this.method()形式绕过代理),跨 Bean 调用才能被拦截。 - 若项目同时使用
CatAopService这类通用切面(参考 integration/spring-aop/cat-aop/CatAopService.java,其@Around("@annotation(CatAnnotation)")会把任意标注CatAnnotation的方法包装成method类型事务),注意与本文注解的拦截规则相互独立、互不影响,可按需选用其一或并存。
九、性能影响与使用建议
原文档明确提示注解方式「会有性能影响」,结合前文源码分析,给出如下使用建议:
- 注解粒度控制:优先在方法入口、跨组件调用边界(缓存、RPC、SQL)埋点,避免在超高频循环体内层方法上加注解。
- 善用 name 聚合:URL、Dubbo 调用场景务必设置合理的
name/callApp/callServer聚合维度,否则动态参数会造成事务名爆炸,反而加重 CAT 服务端压力——这正是CatHttpRequestTransaction存在的意义。 - 保留字规避:自定义
type时不要使用URL、MVC、Service、SQL等 CAT 预留类型(见 CatTransaction.java 注释)。 - 与 Filter 分工明确:Filter 负责请求级 URL 埋点(配置在 web.xml),注解负责方法级/跨服务埋点,两者配合形成「请求 → 方法 → 缓存/DB/Dubbo」的多层监控视图。
十、小结
springMVC-AOP集成方案把 CAT 埋点从「手写 API 调用」升级为「声明式注解」,本文四枚注解的职责可归纳如下:
| 注解 | 适用场景 | Transaction 类型 | 关键属性 |
|---|---|---|---|
CatTransaction | 任意业务方法 | 自定义(默认Handler) | type、name |
CatCacheTransaction | 缓存读写 | Cache.Redis | name、server |
CatHttpRequestTransaction | URL 聚合 | 配合 Filter 改写cat-page-uri | name(默认type=URL) |
CatDubboClientTransaction | Dubbo 客户端调用 | Call | callApp、callServer(必填) |
配合CatMybatisInterceptor的 SQL 埋点,即可在 SpringMVC 项目中快速构建「URL / 方法 / Cache / SQL / Dubbo」全链路监控。相关可复用的切面与注解源码均位于 integration/springMVC-AOP 目录,部署与客户端配置细节可进一步参考 CAT部署.txt。
- 可观测性
- 指标监控
- 告警
- APM
- 后端
- 链路追踪
【免费下载链接】cat
CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
相关推荐
CAT Go 客户端(gocat)接入指南:基于 CGO 的监控埋点实践
CAT Go 客户端(gocat)接入指南:基于 CGO 的监控埋点实践 本篇技术指南围绕 CAT 监控平台的多语言客户端家族中 Go 语言实现 gocat 展
可观测性指标监控告警APM后端链路追踪SurrealDB监控告警新范式:基于Prometheus/Grafana的零侵入实时方案
SurrealDB监控告警新范式:基于Prometheus/Grafana的零侵入实时方案 SurrealDB 是一个基于 Rust 的高性能、可扩展的关系型数
数据库后端分布式数据库文档数据库图数据库嵌入式数据库KV存储5个Metrics注解使用技巧:零侵入式实现应用指标埋点
5个Metrics注解使用技巧:零侵入式实现应用指标埋点 Metrics是一个强大的Java应用监控库,通过简单的注解就能实现零侵入式的指标埋点,让开发者轻松掌
可观测性后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考