在 Spring Cloud 中 微服務之間的調用會用到Feign,可是在默認狀況下,Feign 調用遠程服務存在Header請求頭丟失問題。html
首先須要寫一個 Feign請求攔截器,經過實現RequestInterceptor接口,完成對全部的Feign請求,傳遞請求頭和請求參數。java
public class FeignBasicAuthRequestInterceptor implements RequestInterceptor { private static final Logger logger = LoggerFactory.getLogger(FeignBasicAuthRequestInterceptor.class); @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); } } Enumeration<String> bodyNames = request.getParameterNames(); StringBuffer body =new StringBuffer(); if (bodyNames != null) { while (bodyNames.hasMoreElements()) { String name = bodyNames.nextElement(); String values = request.getParameter(name); body.append(name).append("=").append(values).append("&"); } } if(body.length()!=0) { body.deleteCharAt(body.length()-1); requestTemplate.body(body.toString()); logger.info("feign interceptor body:{}",body.toString()); } } }
經過配置文件配置 讓 全部 FeignClient
,來使用 FeignBasicAuthRequestInterceptor
程序員
feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor
也能夠配置讓 某個 FeignClient
來使用這個 FeignBasicAuthRequestInterceptor
spring
feign: client: config: xxxx: # 遠程服務名 connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor
通過測試,上面的解決方案能夠正常的使用;可是出現了新的問題。微信
在轉發Feign的請求頭的時候, 若是開啓了Hystrix, Hystrix的默認隔離策略是Thread(線程隔離策略), 所以轉發攔截器內是沒法獲取到請求的請求頭信息的。併發
能夠修改默認隔離策略爲信號量模式:app
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
但信號量模式不是官方推薦的隔離策略;另外一個解決方法就是自定義Hystrix的隔離策略。ide
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); } @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; 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調用丟失請求頭的問題就解決的了 。測試
https://blog.csdn.net/zl1zl2zl3/article/details/79084368
https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.0.RC2/reference/html/
歡迎掃碼或微信搜索公衆號《程序員果果》關注我,關注有驚喜~