为 Dubbo 服务接入 CAT 监控:cross 报表、依赖分析与全链路 Trace 实战
2026/9/20 14:49:09 网站建设 项目流程

为 Dubbo 服务接入 CAT 监控:cross 报表、依赖分析与全链路 Trace 实战

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

导读

本文围绕本仓库integration/apache-dubbo目录下的 Dubbo 与 CAT 整合插件展开,讲解如何以最小的接入成本(仅引入一个 Maven 依赖)为基于 Dubbo 的微服务系统开启调用监控,使每一次 RPC 调用自动产生跨服务(cross)报表、依赖分析(dependency)、服务端 matrix 以及调用链路 trace 数据。读完本文,你将掌握该插件的接入方式、手动启停 API、底层 Filter 拦截与消息树上下文透传原理,以及两类错误打点的分类口径,可直接用于排查 Dubbo 调用的耗时与异常问题。

一、插件定位:一行依赖带来的监控能力

该插件名为cat-monitor,其设计目标非常聚焦:监控当前系统 Dubbo 调用的执行情况,包括耗时以及异常统计。与手工埋点不同,它基于 Dubbo 的 Filter 扩展机制自动生效,业务代码零侵入。

按官方接入说明,只需在 Maven 中引入插件包:

<dependency> <groupId>net.dubboclub</groupId> <artifactId>cat-monitor</artifactId> <version>0.0.6</version> </dependency>

That's All!

添加以上依赖之后,CAT 服务端即会出现以下报表(插件自身说明,trace 信息在 CAT 1.0.8 及以后版本提供):

  • cross 报表:跨服务调用统计,展示调用方与被调用方之间的调用量、耗时、成功率;
  • dependency 报表:服务依赖关系图,用于梳理服务间调用拓扑;
  • 服务端 matrix 报表:服务端处理矩阵,观察各接口的响应时间分布;
  • 调用链路 trace 信息:从消费端到提供端的完整消息树串联,可用于单次调用的全链路追踪。

需要说明的是,仓库中实际上维护了两套同名插件实现,分别面向不同 Dubbo 版本线:

  • integration/apache-dubbo:基于org.apache.dubbo:dubbo:2.7.15的新版实现,其 pom.xml 将 Dubbo 声明为provided作用域,依赖运行时环境自带;
  • integration/dubbo:基于com.alibaba.dubbo(2.x 时代)的旧版实现,源码结构与新版本基本一致,仅包名不同(如com.alibaba.dubbo.common.Constants对应新版的org.apache.dubbo.common.constants.CommonConstants)。

因此接入时需按自身 Dubbo 版本选择对应实现:使用 Apache Dubbo 2.7.x 请参考integration/apache-dubbo,使用阿里 Dubbo 2.x 请参考integration/dubbo

二、手动开启 / 关闭监控

插件默认在引入依赖后即自动生效,同时提供全局开关 API,便于在特殊场景(如灰度验证、故障期间临时关闭监控)下动态控制:

DubboCat.disable(); // 关闭 dubbo cat 监控 DubboCat.enable(); // 开启 dubbo cat 监控

该开关的实现位于 DubboCat.java,核心逻辑如下:

public class DubboCat { private static boolean isEnable = true; public static void disable() { isEnable = false; } public static void enable() { isEnable = true; } public static boolean isEnable() { boolean isCatEnabled = false; try { isCatEnabled = Cat.getManager().isCatEnabled(); } catch (Throwable e) { CatLogger.getInstance().error("[DUBBO] Cat init error.", e); } return isCatEnabled && isEnable; } }

从源码可以看到两个关键细节:

  1. isEnable()双重校验:既要插件自身开关为开启状态,又要求 CAT 客户端全局可用(Cat.getManager().isCatEnabled())。若 CAT 客户端初始化失败或未配置,监控会自动失效而不会影响 Dubbo 调用本身;
  2. 开关是静态变量,进程内全局生效;当 CAT 客户端初始化抛出异常时,插件会记录[DUBBO] Cat init error.日志后降级为不采集,体现了"监控失败不影响业务调用"的设计原则。

三、核心原理:基于 Dubbo Filter 的自动埋点

插件的自动埋点能力完全依赖 Dubbo 的 SPI 扩展机制。核心过滤器 CatTransaction.java 通过@Activate注解同时激活在 PROVIDER 和 CONSUMER 两侧,并设置order = -9000抢占过滤链的最前端:

@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, order = -9000) public class CatTransaction implements Filter {

order = -9000意味着该 Filter 在调用链中尽可能早执行,从而能捕获最完整的调用耗时(包含后续所有过滤器与真实调用的时间)。整个invoke流程的核心步骤为:

  1. 开关校验:若DubboCat.isEnable()为 false,直接放行原调用,不产生任何额外开销;

  2. 确定调用方向:根据invoker.getUrl()side参数判断当前是消费端还是提供端,并据此决定 CAT Transaction 的类型:

    • 消费端:CatConstants.CROSS_CONSUMER = "PigeonCall"
    • 提供端:CatConstants.CROSS_SERVER = "PigeonService"

    两个类型常量定义在 CatConstants.java 中。这里复用了 CAT 原有 RPC 框架(Pigeon)的消息命名,目的是让 cross 报表在服务端天然兼容、直接展示;

  3. 创建 Transaction 并记录 cross 明细loggerName接口简单名.方法名拼接而成(如UserService.getUserById),作为 cross 报表中"调用名称"的维度;

  4. 调用真实业务逻辑:执行invoker.invoke(invocation),根据结果决定 Transaction 的成功或失败状态;

  5. finally 收尾transaction.complete()完成埋点并清理 ThreadLocal 上下文。

3.1 消费端 cross 明细(PigeonCall)

消费端侧在发起调用前,通过createConsumerCross打点三个 Event,用于 cross 报表的调用方视角统计:

Event 类型常量含义
CONSUMER_CALL_APPPigeonCall.app被调用的提供方应用名
CONSUMER_CALL_SERVERPigeonCall.server提供方主机(url.getHost()
CONSUMER_CALL_PORTPigeonCall.port提供方端口(url.getPort()

对应源码见 CatTransaction.java。这三个 Event 会被transaction.addChild挂到当前 Transaction 下,随消息树一起上报。

3.2 提供端 cross 明细(PigeonService)

提供端侧在收到请求后,通过createProviderCross打点两个 Event(见 CatTransaction.java):

Event 类型常量含义
PROVIDER_CALL_APPPigeonService.app调用方(消费端)应用名
PROVIDER_CALL_SERVERPigeonService.client调用方主机(RpcContext.getContext().getRemoteHost()

其中消费端应用名的获取依赖 RPC 上下文中透传的application参数(见下文 AppNameAppendFilter);若拿不到,则降级为远程主机:远程端口形式,保证 cross 报表仍有数据可看。

四、异常分类:三类 Error 打点

插件对 Dubbo 调用的异常做了分类统计,共定义了三个 Event 类型(见 CatTransaction.java):

Event 类型常量触发场景
DUBBO_BIZ_ERROR业务异常服务端业务代码抛出的非 RPC/Remoting 异常
DUBBO_TIMEOUT_ERROR调用超时RpcException且根因(cause)为TimeoutException
DUBBO_REMOTING_ERROR网络/远程通信异常RpcException但非超时,或RemotingException及其子类

判定逻辑位于 CatTransaction.java:调用返回后若result.hasException(),则根据异常类型创建对应 Event,event.setStatus(result.getException())记录异常对象,同时将 Transaction 状态置为异常类的简单类名;若无异常则transaction.setStatus(Message.SUCCESS)

另外值得注意的两点实现细节:

  • 异步调用的特殊处理:若RpcUtils.isAsync判定为异步调用,插件不会判断返回结果中的异常(源码注释明确说明这会阻塞接口,因为AsyncRpcResult.hasException会触发 future.get),直接标记为成功;
  • 异常兜底:在catch (RuntimeException e)分支中,若init已成功,会调用Cat.logError(e)记录错误日志并同样打点分类 Event;若发生异常时连初始化都未完成,则仅透传异常,保证监控自身故障不影响调用方。

五、链路串联:消息树上下文在 RPC 间透传

要形成完整的调用链路 trace,必须把"消费端发起的 Transaction"与"提供端接收的 Transaction"串到同一棵消息树上。插件通过 Dubbo 的RpcContextattachment 机制完成上下文透传,核心是 CAT 客户端Cat.Context的三个字段(见 Cat.java 中定义的ROOTCHILDPARENT常量):

_catRootMessageId —— 根消息 ID _catChildMessageId —— 子消息 ID _catParentMessageId —— 父消息 ID

具体流程(见 CatTransaction.java):

  1. 消费端通过Cat.logRemoteCallClient(context)生成新的消息树上下文;
  2. setAttachment把 ROOT / CHILD / PARENT 三个 ID 写入RpcContext的 attachments,随 RPC 请求发送到提供端;
  3. 提供端在initContext时,从RpcContext.getContext().getAttachments()中取出这三个 ID 还原Cat.Context,再调用Cat.logRemoteCallServer(context)将当前消息树挂接到上一跳,从而完成跨进程的链路串联。

上下文对象DubboCatContext使用ThreadLocal<Cat.Context>保存(静态字段CAT_CONTEXT),并在 finally 中CAT_CONTEXT.remove()清理,避免线程池复用导致的上下文串扰。

六、应用名透传:让 cross 报表"指名道姓"

cross 报表的价值在于展示"谁调了谁",因此调用双方的应用名至关重要。插件通过两个组件共同保证应用名的正确性:

6.1 消费端:AppNameAppendFilter

AppNameAppendFilter.java 仅激活在 CONSUMER 侧,在发起调用前把当前提供方 URL 中的application参数写入 RpcContext attachment,使下游能识别调用方应用:

@Activate(group = {CommonConstants.CONSUMER}) public class AppNameAppendFilter implements Filter { @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { RpcContext.getContext().setAttachment( CommonConstants.APPLICATION_KEY, invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY)); return invoker.invoke(invocation); } }

6.2 提供端:Registry 包装获取服务端应用名

CatRegistryFactoryWrapper.java 通过包装 Dubbo 的RegistryFactory,在服务提供方注册 URL 时追加一个自定义参数serverApplicationName(常量定义见 CatConstants.java),值为提供方自身的application

private URL appendProviderAppName(URL url) { String side = url.getParameter(CommonConstants.SIDE_KEY); if (CommonConstants.PROVIDER_SIDE.equals(side)) { url = url.addParameter(CatConstants.PROVIDER_APPLICATION_NAME, url.getParameter(CommonConstants.APPLICATION_KEY)); } return url; }

RegistryWrapperregister/unregister/lookup等关键操作上统一调用appendProviderAppName做 URL 增强。这样消费端拿到提供方地址后,即可通过url.getParameter(CatConstants.PROVIDER_APPLICATION_NAME)直接读出被调服务应用名,无需额外配置;若该参数缺失,getProviderAppName会降级为从接口全限定名中截取包名前缀作为应用名(见 CatTransaction.java),保证数据不中断。

说明:CatRegistryFactoryWrapper的使用需要额外配置注册中心工厂的包装扩展(通过 Dubbo SPI 的qos-registry-factory或 XML 中显式指定),接入时请结合自身 Dubbo 配置方式确认该环节是否生效;核心的 Filter 埋点(第三、四、五节)则无需任何额外配置,随依赖自动激活。

七、从入门到排查:接入路径小结

  1. 确认 Dubbo 版本线:Apache Dubbo 2.7.x 使用 integration/apache-dubbo,阿里 Dubbo 2.x 使用 integration/dubbo;
  2. 引入依赖:在服务提供方和消费方应用(尤其是 RPC 入口应用)的 pom.xml 中加入net.dubboclub:cat-monitor:0.0.6
  3. 确认 CAT 客户端配置:插件依赖com.dianping.cat:cat-client(pom.xml 中声明 2.0.0),需保证各应用已正确接入 CAT 客户端(域名、服务端地址等),否则Cat.getManager().isCatEnabled()为 false,监控不会生效;
  4. 观察报表:部署后即可在 CAT 服务端查看 cross、dependency、matrix 与 trace 数据,验证耗时与异常统计是否符合预期;
  5. 动态控制:特殊场景下可通过DubboCat.disable()/DubboCat.enable()在不重启应用的情况下启停监控。

通过上述步骤,Dubbo 服务的调用耗时、超时、业务异常、网络异常即可自动沉淀为可检索、可告警的监控数据,为微服务架构下的故障定位与服务治理提供第一手依据。

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

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询