背景
在微服务中,我们通常使用open-feign调用其他依赖的服务,我们会传递一些上下文信息,如traceId,token等。正常情况下我们可以直接从当前线程上下文。当使用了Hystrix之后,如果使用了线程池模式进行线程隔离,上下文就会丢失。
HystrixPlugins
自定义线程策略
Hystrix提供了插件机制,我们可以在提交任务的时候拦截处理上下文信息。 自定义一个线程池策略即可
@Slf4j
@Component
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
public FeignHystrixConcurrencyStrategy() {
try {
HystrixConcurrencyStrategy delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (delegate instanceof FeignHystrixConcurrencyStrategy) {
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
// 打印日志
if (log.isDebugEnabled()) {
log.debug(
"Current Hystrix plugins configuration is [concurrencyStrategy [{}], eventNotifier [{}], metricPublisher [{}], propertiesStrategy [{}]]",
delegate,
eventNotifier,
metricsPublisher,
propertiesStrategy);
log.debug("Registering FeignWithHystrix Concurrency Strategy.");
}
HystrixPlugins.reset();
HystrixPlugins instance = HystrixPlugins.getInstance();
instance.registerConcurrencyStrategy(this);
instance.registerCommandExecutionHook(commandExecutionHook);
instance.registerEventNotifier(eventNotifier);
instance.registerMetricsPublisher(metricsPublisher);
instance.registerPropertiesStrategy(propertiesStrategy);
} catch (Exception e) {
log.error("Failed to register FeignWithHystrix Concurrency Strategy", e);
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
// 读取上下文信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 获取当前线程的traceId , 这个线程是tomcat的线程
String traceId = MDC.get("traceId");
log.info(" traceId info -> traceId : {} ", traceId);
return () -> {
try {
// 在子线程(hystrix线程)中添加request上下文信息
RequestContextHolder.setRequestAttributes(requestAttributes);
// 添加traceId
MDC.put("traceId",traceId);
return callable.call();
} finally {
RequestContextHolder.resetRequestAttributes();
}
};
}
}
在拦截器中获取上下文信息
feign发起请求之前做拦截,把要traceId,token放入header中,传到下一个服务。
@Slf4j
public class FeignAuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
//
ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
assert requestAttributes != null;
// 可以获取获取 HttpServletRequest
HttpServletRequest request = requestAttributes.getRequest();
// 链路追踪的TraceId
String traceId = MDC.get("traceId");
log.info("拦截器添加请求头 traceId : {} ",traceId);
// 业务逻辑 模拟认证逻辑
String access_token = UUID.randomUUID().toString();
requestTemplate.header("Authorization",access_token);
requestTemplate.header("traceId",traceId);
}
}