微服务使用feign相互之间调用时,因为feign默认不传输Header,存在session丢失的问题。例如,使用Feign调用某个远程API,这个远程API需要传递一个鉴权信息,我们可以把cookie里面的session信息放到Header里面,这个Header是动态的,跟你的HttpRequest相关,我们选择编写一个拦截器来实现Header的传递,也就是需要实现RequestInterceptor接口,详细代码如下:
1.新建feign配置类FeignConfig.java,它包含一个外部hystrix自定义隔离策略类FeignHystrixConcurrencyStrategy.java继承hystrix隔离策略HystrixConcurrencyStrategy ,并且这个自定义策略类作为bean配置到feign的配置类FeignConfig中
package webapp.conf;import com.netflix.hystrix.HystrixThreadPoolKey;import com.netflix.hystrix.HystrixThreadPoolProperties;import com.netflix.hystrix.strategy.HystrixPlugins;import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;import com.netflix.hystrix.strategy.properties.HystrixProperty;import feign.RequestInterceptor;import feign.RequestTemplate;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.context.annotation.Bean;import org.springframework.web.context.request.RequestAttributes;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Enumeration;import java.util.concurrent.BlockingQueue;import java.util.concurrent.Callable;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class FeignConfig implements RequestInterceptor { @Bean public FeignHystrixConcurrencyStrategy feignHystrixConcurrencyStrategy() { return new FeignHystrixConcurrencyStrategy(); } @Override public void apply(RequestTemplate template) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); if (requestAttributes == null) { return; } HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); EnumerationheaderNames = request.getHeaderNames(); if (headerNames != null) { while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); Enumeration values = request.getHeaders(name); while (values.hasMoreElements()) { String value = values.nextElement(); template.header(name, value); } } } }}class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy { private static final Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class); private HystrixConcurrencyStrategy delegate; 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.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 Callable wrapCallable(Callable callable) { RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); return new WrappedCallable<>(callable, requestAttributes); } @Override public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixProperty corePoolSize, HystrixProperty maximumPoolSize, HystrixProperty keepAliveTime, TimeUnit unit, BlockingQueue 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 getBlockingQueue(int maxQueueSize) { return this.delegate.getBlockingQueue(maxQueueSize); } @Override public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) { return this.delegate.getRequestVariable(rv); } static class WrappedCallable implements Callable { private final Callable target; private final RequestAttributes requestAttributes; WrappedCallable(Callable 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(); } } }}
2.在@FeignClient里配置上面的配置类,如下:
package webapp.userFeign;import org.springframework.cloud.netflix.feign.FeignClient;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import webapp.conf.FeignConfig;@FeignClient(name = "user", fallback = UserFeignFallBack.class, configuration = FeignConfig.class)public interface UserFeign { @RequestMapping(value = "/user/findUser", method = RequestMethod.GET) String findUser(); @RequestMapping(value = "/user/findLUser", method = RequestMethod.GET) String findLUser();}