日志追踪码传递
MDC(Mapped Diagnostic Context,映射调试上下文)是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。MDC 可以看成是一个与当前线程绑定的Map,可以往其中添加键值对。
MDC内部使用的是ThreadLocal所以只有本线程才有效
slf4j MDC源码中 没有用 ThreadLocal 就是普通的Map
使用 ThreadLocal 的是其他的日志框架覆盖 MDC实现
比如 logback 就是在自身的包中覆盖了 org.slf4j 包中的部分类
logback 中的MDC 就是使用 ThreadLocal
在 微服务当前切换 服务传递追踪码
gateway 网关传递
直接在转发的请求头中携带即可
Feign Hystrix
网上资料
网上的写法都是介绍 HystrixRequestVariableDefault
feign 如果有用 hystrix 传递时有涉及到跨线程 需要使用 HystrixRequestVariableDefault 传递
或者重写 wrapCallable 方法
1 2 3 4
| public <T> Callable<T> wrapCallable(Callable<T> callable) { return TtlCallable.get(callable); }
|
还需要按最后 去替换线程池
和 在Configuration配置自定义 HystrixConcurrencyStrategy 替换默认的 HystrixConcurrencyStrategyDefault
1 2 3 4 5 6 7 8
| @Configuration public class HystrixConfig { @Bean public HystrixConcurrencyStrategy requestContextHystrixConcurrencyStrategy() { return new MyHystrixConcurrencyStrategy(); } }
|
此处参考了 https://shanhy.blog.csdn.net/article/details/108668952
实测都不太方便
- HystrixRequestVariableDefault 要用 HystrixContextRunnable 或 HystrixContextCallable创建线程才能在线程间传递数据
- wrapCallable 测试无效
HystrixInvocationHandler
阅读源码后发现 wrapCallable 装饰是在 HystrixInvocationHandler.invoke 之前就调用了
invoke 中 还有一个 HystrixCommand 进行回调
等于是 Hystrix 有两层 调用 第一层时参数还有传递 第二层回调时切换线程导致传递的参数丢失
覆盖 HystrixInvocationHandler 类 调用自定义的 HystrixCommand 进行传参
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public abstract class Lqs1848HystrixCommand<R> extends HystrixCommand<R>{ private String trace; protected Lqs1848HystrixCommand(Setter setter) { super(setter); this.trace = TraceUtils.getTrace(); }
@Override protected R run() throws Exception { TraceUtils.setTrace(trace); try { return runMain(); } finally { TraceUtils.clear(); } } protected abstract R runMain() throws Exception; }
|
dubbo 传递
带隐藏参数即可 在RpcContext 中携带
父子线程传递
网上的方法都是
https://yanglinwei.blog.csdn.net/article/details/113503577
https://segmentfault.com/a/1190000020083061
https://juejin.cn/post/6844904128351567885
https://blog.csdn.net/zlt2000/article/details/99641821
内容都一样也不知道是谁抄谁的
其中最重要的是第一行
package org.slf4j;
要覆盖掉 slf4j 中的MDCAdapter
MDC.mdcAdapter = mdcAdapter;
这个根本就不重要
只要和 logback 一样实现 把 org.slf4j.impl 实现了就行 重写 StaticMDCBinder 即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class StaticMDCBinder {
public static final StaticMDCBinder SINGLETON = new StaticMDCBinder();
private StaticMDCBinder() { }
public MDCAdapter getMDCA() { return new MyMDCAdapter(); }
public String getMDCAdapterClassStr() { return MysdMDCAdapter.class.getName(); } }
|
线程池替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
@Configuration public class ThreadPoolConfig { private int corePoolSize = 8;
private int maxPoolSize = 50;
private int queueCapacity = 1000;
private int keepAliveSeconds = 180;
@Bean(name = "threadPoolTaskExecutor") public ThreadPoolTaskExecutor threadPoolTaskExecutor(){ MyThreadPoolTaskExecutor executor = new MyThreadPoolTaskExecutor(); executor.setMaxPoolSize(maxPoolSize); executor.setCorePoolSize(corePoolSize); executor.setQueueCapacity(queueCapacity); executor.setKeepAliveSeconds(keepAliveSeconds); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); return executor; }
@Bean(name = "scheduledExecutorService") protected ScheduledExecutorService scheduledExecutorService() { ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(corePoolSize, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Threads.printException(r, t); } }; return TtlExecutors.getTtlScheduledExecutorService(scheduledExecutorService); } }
|
@Async
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration @EnableAsync public class AsyncConfig extends AsyncConfigurerSupport {
@Autowired private ThreadPoolTaskExecutor threadPoolTaskExecutor; @Override public Executor getAsyncExecutor() { return threadPoolTaskExecutor; } }
|
MQ传递
我的mq调用都是用的 json格式数据
传输数据时携带上 追踪码即可
总结
跨线程
使用 阿里的 TransmittableThreadLocal
把有提供 回调修饰的方法用 TransmittableThreadLocal提供的工具修饰一下
ThreadLocal<> 替换为 TransmittableThreadLocal<>
再把 所有使用的线程池 换成自己的线程池即可
替换线程池 和 修饰 Callable/Runnable 二者实现其一即可
Hystrix的线程池我就没有替换 而是修饰了 Callable