springCloud微服務系列——OAuth2+JWT模式下的feign+hystrix處理
之前的文章說了一下JWT模式下feign呼叫其他服務的時候,怎麼通過一個方法使得http請求頭的資訊傳遞到被調服務中。當然也可以用@RequestHeader註解,但是這樣並不是太好,因為我們希望有一個全域性的處理。這些問題可以參看之前的文章。
這篇文章將總結的問題是,同樣基於feign模式,但是開啟了hystrix,那麼之前的方式傳遞http請求頭就已經失效了。這裡將介紹如何解決這個問題。
一、hystrix原始碼剖析-追根溯源
為什麼開啟了hystrix就不能獲取到http請求頭資訊了呢,做了一定的原始碼分析。最後總結為下圖:
該圖展示了開啟hystrix後一些比較重要的執行點,hystrix通過命令模式
這裡回顧一下上篇文章中的攔截器,我們看一下獲取http請求的方法
private HttpServletRequest getHttpServletRequest() {
try {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
} catch (Exception e) {
return null;
}
}
再來總結一下整個執行過程hystrixCommond.execute()->return queue().get()[新執行緒]->hystixCommond.run()->feign.ribbon.LoadBalancerFeignClient執行http請求->我們實現的攔截器->getHttpServletRequest->((ServletRequestAttributes)) RequestContextHolder.getRequestAttributes()).getRequest()
整個原因一目瞭然
二、解決方案
怎麼解決問題呢?其實spring cloud自身也有很多地方需要這種跨執行緒的操作,所以它自己有一個類名為HystrixConcurrencyStrategy。我們可以看到它為spring security提供了一個實現SecurityContextConcurrencyStrategy,為sleuth提供了一個實現SleuthHystrixConcurrencyStrategy。我們要做的就是模仿這兩個實現類,實現一個我們自己的HystrixConcurrencyStrategy類。這裡就直接上程式碼了
@Component
public class RequestAttributeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private static final Logger logger = LoggerFactory.getLogger(RequestAttributeHystrixConcurrencyStrategy.class);
private HystrixConcurrencyStrategy delegate;
public RequestAttributeHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof RequestAttributeHystrixConcurrencyStrategy) {
// 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.getInstance().registerConcurrencyStrategy(this);
HystrixPlugins.getInstance()
.registerCommandExecutionHook(commandExecutionHook);
HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
}
catch (Exception e) {
logger.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
}
}
private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
HystrixMetricsPublisher metricsPublisher,
HystrixPropertiesStrategy propertiesStrategy) {
if (logger.isDebugEnabled()) {
logger.debug("Current Hystrix plugins configuration is ["
+ "concurrencyStrategy [" + this.delegate + "]," + "eventNotifier ["
+ eventNotifier + "]," + "metricPublisher [" + metricsPublisher + "],"
+ "propertiesStrategy [" + propertiesStrategy + "]," + "]");
logger.debug("Registering Sleuth Hystrix Concurrency Strategy.");
}
}
@Override
public <T> Callable<T> wrapCallable(Callable<T> callable) {
if (callable instanceof HttpRequestWrappedCallable) {
return callable;
}
Callable<T> wrappedCallable = this.delegate != null
? this.delegate.wrapCallable(callable) : callable;
if (wrappedCallable instanceof HttpRequestWrappedCallable) {
return wrappedCallable;
}
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
return new HttpRequestWrappedCallable<>(callable, requestAttributes);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixProperty<Integer> corePoolSize,
HystrixProperty<Integer> maximumPoolSize,
HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,
keepAliveTime, unit, workQueue);
}
@Override
public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
HystrixThreadPoolProperties threadPoolProperties) {
return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
}
@Override
public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
return this.delegate.getBlockingQueue(maxQueueSize);
}
@Override
public <T> HystrixRequestVariable<T> getRequestVariable(
HystrixRequestVariableLifecycle<T> rv) {
return this.delegate.getRequestVariable(rv);
}
static class HttpRequestWrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public HttpRequestWrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
this.target = target;
this.requestAttributes = requestAttributes;
}
@Override
public T call() throws Exception {
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
return target.call();
}
finally {
RequestContextHolder.resetRequestAttributes();
}
}
}
}