上一篇说了关于MDC跨线程为null的理解,而本讲主要说一下,如何去解决它,事实上,Hystrix为我们留了这个口,我们只需要继承HystrixConcurrencyStrategy,然后重写wrapCallable
方法,再把这个重写的对象注册到Hystrix里就可以了,跨线程共享数据,可以使用阿里的 transmittable-thread-local组件,如果只是共离MDC的话,可以自己写个组件就行。
一 ThreadMdcUtil用来同步MDC对象
public class ThreadMdcUtil {
public static <T> Callable<T> wrap(final Callable<T> callable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
}
else {
MDC.setContextMap(context);
}
try {
return callable.call();
}
finally {
MDC.clear();
}
};
}
public static Runnable wrap(final Runnable runnable, final Map<String, String> context) {
return () -> {
if (context == null) {
MDC.clear();
}
else {
MDC.setContextMap(context);
}
try {
runnable.run();
}
finally {
MDC.clear();
}
};
}
}
重写HystrixConcurrencyStrategy,将主线程的MDC传入Hystrix建立的新线程
/**
* 线程上下文传递,hystrix的相关实现有兴趣可以看源码, hystrix提供了这个口子可以处理线程间传值问题,这里不做过多赘述
*/
public class RequestContextHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
@Override
public <T> Callable<T> wrapCallable(final Callable<T> callable) {
// 使用阿里的 TtlCallable 重新包一层,解决线程间数据传递问题
// return TtlCallable.get(callable);
// 使用自定义的包装对象,将当前mdc复制到Hystrix新线程中
return ThreadMdcUtil.wrap(callable, MDC.getCopyOfContextMap());
}
}
注册我们的RequestContextHystrixConcurrencyStrategy策略到Hystrix
@Configuration
@Slf4j
public class HystrixCircuitBreakerConfiguration {
@PostConstruct
public void init() {
HystrixPlugins.getInstance().registerConcurrencyStrategy(
new RequestContextHystrixConcurrencyStrategy());
}
}