记一次fiegn拦截器传递token的一个坑,发生甚么事了?
事情是这样的,今天调试调用一个微服务的项目,但是发现在调用的过程中发现内部调用服务的时候 token 没有传递过去。
先看下yaml的配置:
feign:
hystrix:
enabled: true # 开启熔断
client:
config:
default:
connectTimeout: 100000
readTimeout: 100000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
关键在于开启熔断,此时feign接口熔断机制为:线程模式,熔断机制正常
使用RequestInterceptor拦截器
@Configuration
public class FeignConfiguration implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes)
RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
}
@Bean
public Logger.Level config(){
return Logger.Level.FULL;
}
}
拦截没有啥问题,但是报错了,如下:
啪的一下很快就报错了哈,我都没反应过来
出现该错误原因:
在feign调用之前,我给他开启了一个拦截器 RequestInterceptor实现类 里面有使用到ServletRequestAttributes 获取请求数据
当feign开启熔断模式的时候,feign 调用会失败 (feign: hystrix: enabled: true)
原因:feign 使用的是线程池模式,当开启熔断的时候,feign 所在的服务端不在同一个线程,这时attributes取到的将会是空值
内部应用大致是 Thread 的线程,开启熔断以后请求的线程发生了变化,导致线程不一致了,所以 token 也没有传递过去。
解决方案:
将hystrix熔断方式:线程模式改为信号量模式
feign:
hystrix:
enabled: true
client:
config:
default:
connectTimeout: 100000
readTimeout: 100000
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
strategy: SEMAPHORE
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
关键所在: strategy: SEMAPHORE
可以修改默认隔离策略为信号量模式:
加上线程信号量即可完美的解决,但是官方并不推荐这么使用。
推荐使用:
自定义策略
HystrixConcurrencyStrategy 是提供给开发者去自定义hystrix内部线程池及其队列,还提供了包装callable的方法,以及传递上下文变量的方法。
所以可以继承了HystrixConcurrencyStrategy,用来实现了自己的并发策略。
@Component
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class);
private HystrixConcurrencyStrategy delegate;
public FeignHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
// Welcome to singleton hell...
return;
}
HystrixCommandExecutionHook commandExecutionHook =
HystrixPlugins.getInstance().getCommandExecutionHook();
HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
HystrixPropertiesStrategy propertiesStrategy =
HystrixPlugins.getInstance().getPropertiesStrategy();
this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);
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 Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (log.isDebugEnabled()) {
log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
+ this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
+ metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new WrappedCallable<>(callable, requestAttributes);
参考链接
https://segmentfault.com/a/1190000021060393