问题
在 Spring Cloud 中 微服务之间的调用会用到 Feign, 但是在默认情况下, Feign 调用远程服务存在 Header 请求头丢失问题.
解决方案
首先需要写一个 Feign 请求拦截器, 通过实现 RequestInterceptor 接口, 完成对所有的 Feign 请求, 传递请求头和请求参数.
Feign 请求拦截器
- 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
- feign:
- client:
- config:
- xxxx: # 远程服务名
- connectTimeout: 5000
- readTimeout: 5000
- loggerLevel: basic
- requestInterceptors: com.leparts.config.FeignBasicAuthRequestInterceptor
经过测试, 上面的解决方案可以正常的使用; 但是出现了新的问题.
在转发 Feign 的请求头的时候, 如果开启了 Hystrix, Hystrix 的默认隔离策略是 Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的.
可以修改默认隔离策略为信号量模式:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
但信号量模式不是官方推荐的隔离策略; 另一个解决方法就是自定义 Hystrix 的隔离策略.
自定义策略
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/
- -- END --
每一个 "在 看", 都是对我最大的肯定 !
来源: http://www.tuicool.com/articles/YjUZraQ