栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java > SpringBoot

Feign 调用丢失Header的解决方案

SpringBoot 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Feign 调用丢失Header的解决方案

问题

在 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 headerNames = request.getHeaderNames();
 if (headerNames != null) {
     while (headerNames.hasMoreElements()) {
  String name = headerNames.nextElement();
  String values = request.getHeader(name);
  requestTemplate.header(name, values);
     }
 }
 Enumeration 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  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();
     }
 }
    }
}

致此,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/

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号