Spring Cloud大家使用的都比较熟悉了,Feign也是我们最常用的服务间调用的方式,最近在工作中遇到这样一个场景。
web端调用A服务,A服务调用B服务。
A服务将web端通过header中的token解析,获取到user信息,A调用B服务时需要将token或者用户信息传递给B服务。
这时候有些人会说,直接把user作为一个对象传递就可以了,但是这种做法导致每个接口都需要传递user对象,非常麻烦。
我们的做法是将token或者user信息统一封装在feign的header中传递给B服务,如果没有开启hystrix,这种方式一点问题也没有,但是开启了hytrix后,出现了大问题!!
我们分两部分来看这个问题:1、FeignInterceptor的实现;2、重写HystrixConcurrencyStrategy
1、FeignInterceptor的实现
public class FeignInterceptor implements RequestInterceptor {
//feign调用时需要去除的header
List<String> excludeHeaders = Arrays.asList(new String[]{"referer","Content-Length"});
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(Constants.FEIGN_CALL_FLAG, Constants.FEIGN_CALL_FLAG);
//直接从request中获取token信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
boolean continueFlag = false;
String name = headerNames.nextElement();
System.out.println("-----------"+name);
for(String excludeHeader: excludeHeaders){
if(name.equalsIgnoreCase(excludeHeader)){
continueFlag = true;
break;
}
}
if (continueFlag){
continue;
}
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
requestTemplate.header(name, value);
}
}
}
}
}
以上代码比较简单,我们定义一个FeignInterceptor实现RequestInterceptor,在通过feign调用的时候,会自动进入此拦截器,在此拦截器中,将token或者user信息放置到header中。
其中需要注意的是,header中的Content-Length是不能放置的,会导致参数长度异常,从而导致服务调用报错。
2、重写HystrixConcurrencyStrategy
开启hystrix后,Hystrix内部提供了两种模式执行逻辑:信号量、线程池。但是,spring cloud官方是不推荐信号量这种方式的,我们只能采用线程池这种方式。
线程池这种方式,是开启了另外一个线程来进行feign调用的,主线程中的request是传递不到新开启线程中的,所以,我们要重写HystrixConcurrencyStrategy。
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
private HystrixConcurrencyStrategy delegate;
public FeignHystrixConcurrencyStrategy() {
try {
this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
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) {
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);
}
@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 WrappedCallable<T> implements Callable<T> {
private final Callable<T> target;
private final RequestAttributes requestAttributes;
public WrappedCallable(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();
}
}
}
}
至此,feign调用开启hystrix后heder传递完美解决。
参考:https://blog.csdn.net/crystalqy/article/details/79083857 feign调用session丢失解决方案