上一篇说了关于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());
	}
}

运行结果,使用openFeign时,已经共享了traceId这个数据值

springboot~openfeign开启熔断之后MDC为null的解决

发表回复