news 2026/9/20 15:18:03

CAT 切面注解埋点实战:SpringMVC 环境下基于 AOP 的零侵入监控方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CAT 切面注解埋点实战:SpringMVC 环境下基于 AOP 的零侵入监控方案
  • 可观测性
  • 指标监控
  • 告警
  • APM
  • 后端
  • 链路追踪

【免费下载链接】cat

CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。

项目地址:https://gitcode.com/gh_mirrors/ca/cat
点击查看免费下载

本指南围绕 CAT 项目提供的springMVC-AOP集成方案展开,讲解如何以切面注解方式快速对系统进行埋点:只需在需要监控的类或方法上添加注解,即可自动生成 CAT Transaction 上报,同时配合 Filter 完成 URL 请求级埋点与动态 URL 聚合。读完本文,你将掌握CatTransactionCatCacheTransactionCatHttpRequestTransactionCatDubboClientTransaction四类注解的完整用法、底层切面实现原理,以及配套的 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:同时配置REQUESTFORWARD,保证直接请求与转发后的请求都能被拦截上报。
  • 从当前仓库源码看,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。源码注释特别提醒:URLMVCServiceSQL是 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 兜底:无论成功失败都在finallycomplete(),保证 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:事务名,默认取被拦截方法名(如getputdelete)。
  • 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(); }

上报的数据结构为:

上报项类型
Transactiontype=Cache.Redisname=方法名或注解 name
Eventtype=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(); }

上报的数据结构为:

上报项类型
Transactiontype=Callname=方法名或注解 name
Eventtype=Call.appcallApp()(被调应用)
Eventtype=Call.servercallServer()(被调服务)

这三个维度组合后,可在 CAT 中分析「本应用 → 某应用 → 某服务」的调用量、耗时与错误率。

使用示例(原文档示例)

@CatDubboClientTransaction(callApp = "orders", callServer = "orderServer") public List<Long> getOrdersByUser() { // 内部发起 Dubbo 调用 }

七、配套 SQL 埋点:CatMybatisInterceptor

切面注解体系之外,springMVC-AOP目录还附带了一份 MyBatis SQL 埋点的完整实现 CatMybatisInterceptor.java,用于补全「SQL」这一 CAT 预留事务类型。

拦截器实现要点

该拦截器通过 MyBatis@Intercepts注解拦截Executorupdatequery两个方法:

@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类型事务),注意与本文注解的拦截规则相互独立、互不影响,可按需选用其一或并存。

九、性能影响与使用建议

原文档明确提示注解方式「会有性能影响」,结合前文源码分析,给出如下使用建议:

  1. 注解粒度控制:优先在方法入口、跨组件调用边界(缓存、RPC、SQL)埋点,避免在超高频循环体内层方法上加注解。
  2. 善用 name 聚合:URL、Dubbo 调用场景务必设置合理的name/callApp/callServer聚合维度,否则动态参数会造成事务名爆炸,反而加重 CAT 服务端压力——这正是CatHttpRequestTransaction存在的意义。
  3. 保留字规避:自定义type时不要使用URLMVCServiceSQL等 CAT 预留类型(见 CatTransaction.java 注释)。
  4. 与 Filter 分工明确:Filter 负责请求级 URL 埋点(配置在 web.xml),注解负责方法级/跨服务埋点,两者配合形成「请求 → 方法 → 缓存/DB/Dubbo」的多层监控视图。

十、小结

springMVC-AOP集成方案把 CAT 埋点从「手写 API 调用」升级为「声明式注解」,本文四枚注解的职责可归纳如下:

注解适用场景Transaction 类型关键属性
CatTransaction任意业务方法自定义(默认Handlertypename
CatCacheTransaction缓存读写Cache.Redisnameserver
CatHttpRequestTransactionURL 聚合配合 Filter 改写cat-page-uriname(默认type=URL
CatDubboClientTransactionDubbo 客户端调用CallcallAppcallServer(必填)

配合CatMybatisInterceptor的 SQL 埋点,即可在 SpringMVC 项目中快速构建「URL / 方法 / Cache / SQL / Dubbo」全链路监控。相关可复用的切面与注解源码均位于 integration/springMVC-AOP 目录,部署与客户端配置细节可进一步参考 CAT部署.txt。

  • 可观测性
  • 指标监控
  • 告警
  • APM
  • 后端
  • 链路追踪

【免费下载链接】cat

CAT 作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。

项目地址:https://gitcode.com/gh_mirrors/ca/cat
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 15:17:23

霍普金森杆实验数据处理程序的设计与实现

简介&#xff1a;这份资源是一篇关于霍普金森杆实验数据处理程序设计与实现的学术论文PDF&#xff0c;适合材料动力学、冲击力学及相关军事工程领域的研究人员、工程师和高年级学生阅读。内容系统阐述了SHPB实验原理、入射波/反射波/透射波的分离难点&#xff0c;并给出了基于V…

作者头像 李华
网站建设 2026/9/20 15:16:48

SQL注入实战:sqli-labs靶场6-10关盲注与文件写入技巧详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 15:15:19

Qt 6.8 LTS与Qt for MCUs 2.9全栈嵌入式GUI技术解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 15:15:10

Kimi 论文调研老断在 Key 上?Base URL 填 TaoToken 的 API 地址

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 15:14:03

TRAE 智能体不走内置模型,改走 TaoToken 通道行不行

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 15:12:59

LLM推理显存估算:从KV Cache到量化部署的完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华