大家好,我叫大鸡腿,大家可以关注下我,会持续更新技术文章还有人生感悟,感谢~
在很多工作被框架实现之后,我们会越来越少了解里面的原理。
原理这种东西,举个栗子形容一下,比如一辆汽车跑不起来了,一个经常修车的老师傅(没有拆过汽车)会跟你说,凭借我多年修车经验,可能是发动机坏了。然后给你换了发动机,结果还是起不来,配件一个一个换。这时一个工程师(了解汽车构造)过来看了一下,根据汽车各个部件的反应以及检测,最后跟你说是某个部件坏了,然后当着你的面把汽车拆出来,给你看确实是那个组件坏了,换完就跑起来了~
废话了很多,就是了解原理可以让我们更加清晰的认识框架,更好的使用。
之前经常在技术群被肥朝哥问我们链路日志日志那么多怎么查看的,是怎么生成traceId的,今天我们来认真的学习一下。
我看了下我们项目里面是没有配置traceId,但是日志里头却有,当时问了下我们架构师,也说不清,后面才发现是zipkin+sleuth来进行日志收集。
%X{X-B3-TraceId:-},%X{X-B3-SpanId:-}
在架构里头
我们可以看到需要中间层去设置traceId以及异步上报到zipkin统计里头
注意一下,这里提供了一个很重要的思路:就是在客户端的时候使用上下文去储存traceId,在远程调用的时候,在header加参数,在服务提供者加到上下文进行缓存。
总结
dubbo我本人不是经常用,大概思路是client和service端使用ThreadLocal去缓存traceId,在rpc上使用RpcContext 上下文传递。跟上面sleuth的原理差不多。
如果自研的话,我们用的是Fegin,在远程调用的时候同样需要获取上下文~
同样我也没有了解或者实践过只能贴一下别人的博客,之后再进行验证:Spring Cloud Feign添加自定义Header
参考文章
主要看下官网的这一段:
除了使用ThreadLocal来缓存traceId之后可以用slfj的MDC来设置参数!
定义一个内部类
static class MyThreadPoolTaskExecutor extends ThreadPoolExecutor {
public MyThreadPoolTaskExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable task) {
Map context = MDC.getCopyOfContextMap();
super.execute(() -> putMap(task, context));
}
private void putMap(Runnable task, Map context) {
MDC.setContextMap(context);
try {
task.run();
} finally {
MDC.clear();
}
}
}
测试demo
public static void main(String[] args) {
MDC.put("traceId",String.valueOf(new Random().nextInt(100)));
MyThreadPoolTaskExecutor executor=new MyThreadPoolTaskExecutor(5,10,1,TimeUnit.SECONDS,new BlockingArrayQueue<>());
for(int i=0;i<5;i++){
Thread thread=new Thread(() -> System.out.println(MDC.getCopyOfContextMap()));
executor.execute(thread);
}
}
运行结果
{traceId=20}
{traceId=20}
{traceId=20}
{traceId=20}
{traceId=20}
解释
MDC.put(“traceId”,String.valueOf(new Random().nextInt(100)));这一步要看各自框架如何实现的,如果像我们公司使用zipkin+sleuth来上报统计日志,估计就是这个X-B3-TraceId咯~。或者ThreadLocal版本也一样,拿到之后丢到各自线程里头的变量就完事了。