背景
最近脉脉上比较多脉友关注的技术面试题目“微服务调用中TraceId是怎么传递的?”,有比较多的同学关注并发表了自己的见解。
TraceId是可以串连一个完整请求的唯一记录id,通过traceId可以关联到整个请求过程中涉及到的上下游通信,包括网关,rpc通信,mq通信链路。traceId总体来说会在两种场景中传递。
TraceId还是很有用的,日常开发中,我们经常通过TraceId来捞服务日志,通过TraceId查看整个请求链路耗时,异常等信息,可以说是日常开发运维中不可缺少的排障手段。
总体来说,traceId会在两种场景中传递。
**
应用内传递TraceId
**
第一种是应用内部传递,这种传递我们一般通过ThreadLocal,InheritableThreadLocal传递,这个不是本文重点,不做具体分析。
跨进程的应用间传递TraceId
第二种是跨进程的应用间传递,这种会涉及到远程rpc通信,mq通信,数据库通信等,这种一般我们需要借助中间件的扩展机制来实现传递TraceId,这个也是本文分析的重点。
OpenFeign调用传递
由于openfeign底层使用http协议作为通信协议,而http的请求报文包含header和body,body一般是用于业务数据交换,而header一般用于交换非业务数据,TraceId就非常适合在header中传输,那么openfeign的header我们需要怎么增加自定义属性呢?这个时候就需要看openfeign的扩展机制了。上文(面试官:OpenFeign十大可扩展组件你知道哪些?)分析openfeign扩展点的文章就提到了feign.RequestInterceptor, 这个机制能够实现修改请求对象的目的,那么可以利用他来传输traceId。下面是一个写traceId拦截器的例子。
通过实现feign.RequestInterceptor接口就可以在发起feign请求前注入traceId并透传给下游服务,是不是超简单。
Dubbo调用传递TraceId
dubbo不像openfeign, dubbo支持多种通信协议,下图是dubbo官方的通信协议图谱,默认有自己的dubbo通信协议,dubbo协议默认使用netty作为通信框架
Dubbo协议虽然是Dubbo框架自定义的协议,但是dubbo协议也有请求头和请求体,Dubbo协议可以使用Attachments对象传输非业务数据,相信使用过dubbo的同学应该会知道这个。通过RpcContext.getContext()拿到当前执行请求的上下文信息,再通过setAttachment方法进行传递。下面是结合Dubbo的过滤器机制统一实现rpc传递。
RocketMq通过消息传递traceId
要知道rocketmq怎么传递traceId,同样需要看下他的消息体,通过org.apache.rocketmq.common.message.Message可知properties属性最适合传递。
生产者最终将在发送消息前会将properties传入requestHeader进行传输,我们也可以写一个拦截器,拦截所有的消息生产者,在properties里加入TraceId。
总结
本文分析了TraceId传输的场景以及在应用间传输的部分实现,通过应用间的传输实践,我们知道了通信框架一般都是可以通过请求报文的请求头来传输TraceId的,而且会使用拦截器来实现通用的传输TraceId。