链路传播
定义的是中间件的Server/Producer端将链路信息注入到载体的行为;以及Client/Consumer如何从载体中抽取链路信息的行为;载体
可以是消息体或者请求体。
背景
上一篇提到通过zipkin:实现zipkin-spring-boot-starter(三)包降低接入链路跟踪门槛以后,开发同学接入的积极性提高,同时也反馈目前支持链路跟踪的中间件满足不了需求
,比如说无法跟踪通过redis实现的轻量级mq以及阿里云的mns等。
但是站在框架维护者的角度考虑:对这类消息中间件的链路跟踪非常棘手,为什么?
因为这类消息中间件没有扩展机制,开发者发送到消息队列中的业务消息就是就是消息的全部。若要对消息进行链路跟踪,那么势必会对消息体进行侵入:将链路信息放到业务消息中。
若侵入用户消息体,那么就要对业务消息体的格式有强要求,这是不行的、会引发出很多问题。
那RocketMQ为什么没有这个问题呢?因为RocketMQ将消息体分成了两个部分:meta信息和业务数据;不管消息体中的业务数据如何变化都可以将链路上下文放到meta信息中,从而实现链路跟踪。
所以现在面临的问题:
- 框架维护者的角度:不能以侵入业务消息体为代价进行链路跟踪
- 业务开发者的角度:需要对此类消息中间件进行链路跟踪
现在是一个两难的局面,只能大家各往后退一步:
- 框架维护者:提供链路信息的提取和注入工具
- 业务开发者:自行在生产端将当前链路信息注入到消息体中;在消费端提取消息体中的链路信息提取出来
嗯,也是一个不错的方案!
zipkin链路信息
聊链路传播之前,我们来看看链路传播到底传播了哪些信息,这是从dubbo链路提取出来的链路信息:
- X-B3-SpanId=c4d1b2bf028149ec
- X-B3-ParentSpanId=bfb6542ce3a8f6ca,
- X-B3-Sampled=1
- X-B3-TraceId=bfb6542ce3a8f6ca
简单解释一下:
- X-B3-SpanId:当前span的id
- X-B3-ParentSpanId:当前span的父spanid
- X-B3-Sampled:采样标识符,即是否上报的zipkin-server端
- X-B3-TraceId:当前trace的id
链路传播
在官方的代码中,链路传播分为以下几个步骤:
1. 定义具体的传播行为
- brave.propagation.Propagation.Setter:将链路信息set到指定的载体中
- brave.propagation.Propagation.Getter:将链路信息从指定的载体中get出来
2. 提取或者注入链路信息
- brave.propagation.Propagation#injector:与Setter结合使用,将内存中的链路信息注入到载体中
- brave.propagation.Propagation#extractor:与Getter结合使用,将载体中的链路信息提取到内存中
ok,进入实操阶段,先看看dubbo链路传播是怎样实现的
官方dubbo链路传播
1. 定义具体的传播行为:将请求体中的attachement作为链路信息的载体,把链路信息直接put到attachment中
static final Setter<DubboClientRequest, String> SETTER =
new Setter<DubboClientRequest, String>() {
@Override public void put(DubboClientRequest request, String key, String value) {
request.putAttachment(key, value);
}
};
2. 提取或者注入链路信息:提取内存中的链路信息并放到载体中
//1. 设置具体的链路传播行为
TraceContext.Injector<DubboClientRequest> injector = tracing.propagation().injector(SETTER);
//2. 从dubbo请求上下文中获取载体信息,即attachment
Map<String, String> attachments = RpcContext.getContext()