dubbo3+sleuth+brave实现链路追踪及traceId未传递或不对应的原因分析

本文讲述了作者在从SpringCloudAlibaba+dubbo2+nacos1.4升级到dubbo3+nacos2的过程中遇到的链路追踪问题,以及通过修改brave.dubbo.TracingFilter解决服务消费者和服务提供者间traceId和spanId传递的问题。
部署运行你感兴趣的模型镜像

问题场景

笔者之前一直使用SpringCloud Alibaba + dubbo2 + nacos1.4进行开发,但是目前naocs2、dubbo3也已经推出有一段时间并逐渐达到生产环境可用状态,所以笔者也希望用最新版本的nacos2及dubbo3尝尝鲜。但在搭建框架的过程中遇到了链路追踪的问题,所以在这里详细记录一下。

SpringCloud Alibaba + dubbo2 + nacos1

笔者基于dubbo2和nacos1的框架的依赖版本如下(这里只展示链路追踪相关依赖):

SpringCloud Alibaba 2.2.5.RELEASE
Springboot 2.3.7.RELEASE
Dubbo 2.7.8 
Nacos 1.4.3
spring-cloud-starter-sleuth 3.0.0
brave-instrumentation-dubbo 5.13.7

基于dubbo2和nacos1,该依赖版本网上都有较为成熟的搭建方案。直接引入依赖,按照度娘上的配置一下yml文件,就能实现链路追踪,配置过程不太困难。这里就不详细介绍了。

SpringCloud Alibaba + dubbo3 + nacos2

在搭建基于dubbo3和nacos2的框架时也希望能实现链路追踪。其中方案一和方案二是目前Dubbo3官方手册列举的实现方案。
方案一:Skywalking
听说是最简单的,但因为要额外部署Skywalking,所以我暂未尝试这种方案。
方案二:OpenTelemetry或brave
我根据dubbo3的文档进行依赖的引入和yml文件的配置,但是发现服务提供者日志正常写入了traceId和spanId,但服务消费者无论traceId还是spanId都没写入,这个在dubbo的GitHub issue中也未有人提问,所以最终也没找到解决方案。
方案三:sleuth+brave
我尝试了沿用SpringCloud Alibaba + dubbo2 + nacos1框架时的sleuth+brave方案实现链路追踪,但我引入sleuth+brave并根据SpringCloud Alibaba + dubbo2 + nacos1框架时的yml文件配置后,发现虽然服务消费者和服务提供者的日志都写入了traceId和spanId但两个服务的traceId不一致,变成各写各的了,整个追踪链条并未正确串联起来。 最终通过查找GitHub上的issue终于找到一个brave的dubbo2扩展能兼容dubbo3的解决方案,在这里感谢@ShenFeng312这位大佬。
GitHub issue的comment链接如下:
https://github.com/apache/dubbo/issues/11650#issuecomment-1446313706

解决方案

笔者用的依赖版本如下:

SpringCloud Alibaba 2021.0.5.0
Springboot 2.7.8
Dubbo 3.2.4 
Nacos 2.2.0
spring-cloud-starter-sleuth 3.1.9
brave-instrumentation-dubbo 5.16.0

解决方案非常简单,只需修改一行源码即可。
修改brave.dubbo.TracingFilter#invoke中的RpcContext.getContext().getAttachments()改为invocation.getAttachments()
修改前:
在这里插入图片描述
修改后:
在这里插入图片描述
PS: 因为是要修改依赖的源码,所以各位读者可以复制该类、修改这行、增加Dubbo的SPI来指向自定义的TracingFilter。又或者自行重新打包jar。这里就不详细叙述了。

原因

根据GitHub上https://github.com/apache/dubbo/issues/11650#issuecomment-1446313706中@ShenFeng312大佬的分析,主要是因为dubbo2和dubbo3的RpcContext中invcation的attachment属性的实现方式改变了导致的。从issue的回复中其实也看到@ShenFeng312大佬也向brave提交了merge request,让brave-instrumentation-dubbo能兼容dubbo3,但是brave的开发人员一直未同意合并,这里就不展开说了,大家有兴趣可以自行去GitHub上看看。

疑问

根据知其然,也要知其所以然的想法,我尝试分析其中的原因,但也产生了一些疑问,以下疑问也会在后续的排查过程中逐一解答。

疑问一:dubbo2中为什么直接用RpcContext.getContext().getAttachments()就能传递traceId而不用invocation.getAttachments()呢?
疑问二:为什么dubbo3中brave.dubbo.TracingFilter继续用RpcContext.getContext().getAttachments()是不行的呢?
疑问三:dubbo3中的RpcContext.getContext().getAttachments()和invocation.getAttachments()有什么区别?

排查过程

追踪brave.dubbo.TracingFilter#invoke方法的try…catch…部分可以看到,最终传递到下一个服务的参数是通过invocation的,而traceId和spanId是保存在invocation的attachment这个map中的。所以我们接下来排查的目标都是检查并验证最终invoke时invocation的attachment中有没有traceId和spanId为准。
在这里插入图片描述

排查疑问一:

在这里插入图片描述
根据上述思路及brave.dubbo.TracingFilter#invoke方法中的注释得知,其实clientHandler.handleSendWithParent(clientRequest, invocationContext)一直都只是在操作RpcContext.getContext()的attachment并未操作invocation中的attachment
在这里插入图片描述
而invocation中的attachment其实是直到invoker.invoke(invocation)时才在org.apache.dubbo.rpc.protocol.AbstractInvoker#invoke方法把RpcContext.getContext()的attachment注入到invocation中的attachment中
在这里插入图片描述
所以通过上面的代码跟踪可以发现,dubbo2中虽然一直都是在操作RpcContext.getContext()的attachment,但会在AbstractInvoker#invoke方法中invoke下一个服务的前一刻把RpcCotext.getContext()的attachment注入到invocation中的attachment中,最终还是通过invocation中的attachment传递traceId给下一个服务的。
综上所述,在dubbo2中通过RpcContext.getContext().getAttachments()来操作RpcContext的attachment最终都会在AbstractInvoker#invoke方法里被注入到invocation中的attachment中,所以dubbo2中是可以通过RpcContext.getContext().getAttachments()来传递traceId的

排查疑问二、三:

追踪dubbo3中的RpcContext.getContext().getAttachments():
在这里插入图片描述
比对dubbo2中的RpcContext.getContext().getAttachments():
在这里插入图片描述
通过比较dubbo3和dubbo2的RpcContext.getContext().getAttachments()的实现,可以发现,dubbo3实现方式完全不同了,这个改动在dubbo3的官方手册中其实是有提及的
在这里插入图片描述
正是因为RpcContext.getContext().getAttachments()的实现改动,dubbo3中RpcContext.getContext().getAttachments()返回的不是RpcContext中的attachment也不是invocation中的attachment。而是把RpcContext中的SERVER_ATTACHMENT、CLIENT_ATTACHMENT复制一份并返回。后面再怎么put参数进RpcContext中的attachment都没有用了,即使后面在AbstractInvoker#invoke方法中把RpcContext中的attachment注入进invocation的attachment也没用了,因为put参数的时候是put进RpcContext的attachment的副本,而不是RpcContext中的attachment本身
而且! 本身dubbo3中AbstractInvoker#invoke方法中把RpcContext中的attachment注入invocation的attachment的实现也变了
在这里插入图片描述
不过这里已经不重要了,因为put参数的时候是put进RpcContext中的attachment的副本并未put进RpcContext的任何属性中。
综上所述,通过上述对源码的分析也解释了疑问二疑问三,解释了dubbo3中的RpcContext.getContext().getAttachments()和invocation.getAttachments()有什么区别和为什么dubbo3中brave.dubbo.TracingFilter继续用RpcContext.getContext().getAttachments()是不行的。

总结分析

总的来说,主要是dubbo3的RpcContext被拆分为四大模块才导致brave的dubbo扩展不能直接用,但是其实改动起来还是比较简单的。上述如果有说的不对的地方,还请各位大佬指正。下面笔者还画了一张图来帮助理解:
在这里插入图片描述
最后,版权归作者所有,任何形式转载请联系作者,谢谢!

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>我们被要求使用Dubbo3和Shenyu实现分布式链路追踪。分布式链路追踪是微服务架构中用于跟踪请求在多个服务间流转的过程,帮助我们定位问题、分析性能。 在Dubbo3中,我们可以通过集成Micrometer者OpenTelemetry等来实现链路追踪数据的生成和导出。而Shenyu作为API网关,同样需要支持链路追踪,并将网关接收到的请求与下游服务(如Dubbo服务)的追踪信息关联起来。 常见的做法是使用OpenTelemetry标准,因为Dubbo3和Shenyu都支持OpenTelemetry。 步骤大概如下: 1. **在Dubbo3服务中集成OpenTelemetry** - 添加相关依赖(如`dubbo-spring-boot-starter`和`opentelemetry-api`,`opentelemetry-sdk`,`opentelemetry-exporter-otlp`等) - 配置OpenTelemetry的导出器(将追踪数据导出到Jaeger、ZipkinOTLP后端等) - 配置Dubbo的Tracing Filter,以生成和传播追踪上下文。 2. **在Shenyu网关中集成OpenTelemetry** - Shenyu支持多种插件,其中就包括OpenTelemetry插件。我们需要在Shenyu中配置OpenTelemetry,使其能够接收传入的追踪上下文(生成新的上下文),并将上下文传播到下游的Dubbo服务。 3. **配置追踪信息的传播** - 确保在Dubbo3和Shenyu之间,追踪上下文(TraceId, SpanId等)通过Dubbo的RPC上下文正确传递。 4. **部署一个追踪后端** - 例如Jaeger、Zipkin者SkyWalking等,用于收集、存储和展示追踪数据。 具体实现细节: ### 1. Dubbo3服务集成OpenTelemetry **添加依赖**(以Maven为例): ```xml <!-- OpenTelemetry SDK --> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-sdk</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-exporter-otlp</artifactId> <version>1.19.0</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-semconv</artifactId> <version>1.19.0</version> </dependency> <!-- Dubbo Spring Boot Starter --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-spring-boot-starter</artifactId> <version>3.0.8</version> </dependency> <!-- Dubbo OpenTelemetry --> <dependency> <groupId>org.apache.dubbo.extensions</groupId> <artifactId>dubbo-opentelemetry</artifactId> <version>1.0.0</version> </dependency> ``` **配置OpenTelemetry**(在`application.properties``application.yml`中): ```properties # 设置服务名 dubbo.application.name=dubbo-service-provider # 开启Tracing dubbo.tracing.enabled=true dubbo.tracing.provider=opentelemetry # OpenTelemetry Exporter配置 otel.exporter.otlp.endpoint=http://localhost:4317 otel.traces.exporter=otlp ``` **代码配置**(如果需要自定义,可以创建配置类): ```java @Configuration public class OpenTelemetryConfig { @Bean public OpenTelemetry openTelemetry() { return OpenTelemetrySdk.builder() .setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder().setEndpoint("http://localhost:4317").build()).build()) .build()) .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance())) .buildAndRegisterGlobal(); } } ``` ### 2. Shenyu网关集成OpenTelemetry Shenyu使用`Apache Shenyu`插件机制,我们可以使用其`opentelemetry`插件。 **添加依赖**(在Shenyu网关项目中): ```xml <dependency> <groupId>org.apache.shenyu</groupId> <artifactId>shenyu-spring-boot-starter-plugin-opentelemetry</artifactId> <version>2.5.0</version> </dependency> ``` **配置**(在`application.yml`中): ```yaml shenyu: plugins: tracing: enabled: true openTelemetry: endpoint: http://localhost:4317 ``` 这样,Shenyu就会将追踪数据导出到同一个OpenTelemetry收集器(后端)。 ### 3. 验证追踪信息 启动JaegerZipkin等后端,然后通过Shenyu网关访问Dubbo服务,查看追踪数据是否完整。 ### 注意事项 - 确保所有服务(包括Shenyu网关)的时间同步,否则追踪时间线可能错乱。 - 确保追踪上下文在服务间正确传递Dubbo3和Shenyu都会自动处理上下文传播,但需要确保它们使用相同的传播协议(如W3C Trace Context)。 - 如果使用Dubbo3的跨进程调用,需要确保调用链的连续性。 通过以上步骤,我们就可以实现基于Dubbo3和Shenyu的分布式链路追踪。 ### 引用说明 [^1]: Dubbo官方文档:https://dubbo.apache.org/zh/docs3-v2/java-sdk/features/observability/tracing/ [^2]: Shenyu官方文档:https://shenyu.apache.org/docs/plugin-center/tracing/opentelemetry §§相关问题§§ 1. 如何配置Dubbo3使用Jaeger作为追踪后端? 2. Shenyu网关支持哪些追踪系统? 3. 在分布式链路追踪中,如何确保上下文在同服务间正确传递?</think>### 使用 Dubbo3 和 Shenyu 实现分布式链路追踪的方法 #### 一、核心原理 分布式链路追踪的核心是**传递上下文信息**(如 TraceID、SpanID),通过 Dubbo3 的 Filter 机制和 Shenyu 的插件体系实现链路追踪。关键技术点: 1. **TraceID 传递**:通过 Dubbo 的 `RpcContext` 传递上下文 2. **数据采集**:使用 OpenTelemetry 标准协议 3. **数据展示**:集成 Zipkin/Jaeger 等可视化工具 #### 二、实现步骤 ##### 1. Dubbo3 服务端配置 **添加依赖**(Maven): ```xml <!-- Dubbo 链路追踪 --> <dependency> <groupId>org.apache.dubbo</groupId> <artifactId>dubbo-tracing-api</artifactId> <version>3.0.8</version> </dependency> <dependency> <groupId>io.opentelemetry</groupId> <artifactId>opentelemetry-api</artifactId> <version>1.19.0</version> </dependency> ``` **配置 Tracing Filter**: ```java // 启用追踪过滤器 @Bean public Filter tracingFilter() { return new TracingFilter(); } // 注册 OpenTelemetry 导出器 @Bean public SpanExporter spanExporter() { return OtlpGrpcSpanExporter.builder() .setEndpoint("http://jaeger:4317") // 替换为实际地址 .build(); } ``` ##### 2. Shenyu 网关配置 **启用 tracing 插件**: ```yaml shenyu: plugins: tracing: enabled: true host: "localhost" port: 9411 # Zipkin 默认端口 ``` **添加请求拦截器**(传递 TraceID): ```java public class TraceIdInterceptor implements HttpRequestInterceptor { @Override public void process(HttpRequest request) { String traceId = MDC.get("traceId"); // 从 SLF4J MDC 获取 if (traceId != null) { request.setHeader("X-B3-TraceId", traceId); } } } ``` ##### 3. 全链路追踪流程 ```mermaid sequenceDiagram Client->>Shenyu: 请求网关 (携带 TraceID) Shenyu->>DubboProvider: 转发请求 (通过 RpcContext 传递 TraceID) DubboProvider->>DubboConsumer: 内部调用 (自动传递上下文) DubboConsumer-->>Shenyu: 返回响应 Shenyu-->>Client: 返回结果 ``` #### 三、关键配置项 | 组件 | 配置项 | 示例值 | 作用 | |------------|----------------------------|-------------------------|--------------------------| | **Dubbo3** | `dubbo.provider.filter` | `tracing` | 启用追踪过滤器 | | | `otel.exporter.otlp.endpoint` | `http://jaeger:4317` | 数据导出地址 | | **Shenyu** | `shenyu.plugin.tracing.enabled` | `true` | 启用链路追踪插件 | | | `shenyu.plugin.tracing.host` | `zipkin-host` | 追踪数据存储地址 | #### 四、验证与调试 1. **日志验证**: ```shell # 查看 Dubbo 日志 grep "TraceID" dubbo.log ``` 2. **Zipkin 界面**: ```shell http://localhost:9411/zipkin # 查看完整调用链 ``` #### 五、注意事项 1. **版本兼容性**: - Dubbo33.0.7 - Shenyu ≥ 2.5.0 2. **上下文传递**: - 确保跨线程时手动传递 `MDC` 上下文 3. **性能影响**: - 采样率建议设置为 0.1(10%请求采样)[^1] [^1]: 阿里巴巴微服务实践:https://developer.aliyun.com/article/781516
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值