spring cloud 问题记录(十六) 使用Feign跨服调用时header请求头中的信息丢失

  • Post author:
  • Post category:其他


写这篇帖子呢其实是因为昨天(2019年7月4号)我同事(大牛)遇到的一个BUG,我也就跟着学习了下。

情况呢很简单,我们项目的权限验证使用的keycloak,而验证体系继承在了gateway,获取用户信息的时候。而用户的部分信息是直接通过gateway获取之后,使用header的方式传输给服务的。而同事在使用A服务调用B服务的时候,发现从request里面获取不到我们自己指定的请求头信息了,其实也就是说在服务调用的时候request的请求头丢失了。而使用的就是Feignclient的调用方式。

好了,问题发现了,当然就得解决咯,其实这个问题很多人都遇到过。往上的解决方案也一大把,我也就梳理总结下:

第一步,做一个请求拦截,把header信息都放到RequestTemplate

/**
 *  功能描述:
 * @author 朱维
 * @date 2019年7月4日
 * @time 下午9:14:52
 * @Path: com.xuebaclass.sato.conf.FeignConfiguration.java
 */
@Component
public class FeignConfiguration implements RequestInterceptor{
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    @Override
    public void apply(RequestTemplate template) {
        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);
                template.header(name, values);

            }
            logger.info("feign interceptor header:{}",template);
        }
    }
}

第二步:Feignclient调用的时候指定configuration


@FeignClient(name = "crm-course", fallback = AutoRenewHystrix.class,configuration=FeignConfiguration.class)
public interface AutoRenewService {
    /**
     * 续排课时
     *
     * @param studentId
     * @param totalNum
     * @return
     */
    @PostMapping("/autoRnnewForOrder")
    public String autoRenewForOrder(@RequestBody Map<String, String> params);
}

第三步:指定


Feign


的隔离策略为:SEMAPHORE

hystrix: 
  command: 
    default: 
      execution: 
        timeout: 
          enabled: true 
        isolation: 
          thread: 
            timeoutInMilliseconds: 600000
          strategy: SEMAPHORE



注意当Feign的隔离策略为THREAD时,由于当使用该隔离策略时,是没办法拿到 ThreadLocal 中的值的,但是RequestContextHolder 源码中,使用了两个ThreadLocal,因此当使用该隔离策略时是没有办法通过RequestContextHolder获取到request对象的,这时如果你还坚持使用THREAD这个隔离策略就需要自定义策略(重写THREAD隔离策略),其他的配置内容一样,代码如下:

/**
 * 自定义并发策略
 * 将现有的并发策略作为新并发策略的成员变量
 * 在新并发策略中,返回现有并发策略的线程池、Queue
 *
 * hystrix.command.default.execution.isolation.strategy=THREAD   Hystrix的默认隔离策略(官方推荐,当使用该隔离策略时,是没办法拿到 ThreadLocal 中的值的,但是
 *                                                               RequestContextHolder 源码中,使用了两个ThreadLocal)
 * hystrix.command.default.execution.isolation.strategy=SEMAPHORE (将隔离策略改为SEMAPHORE 也可以解决这个问题,但是官方并不推荐这个策略,因为这个策略对网络资源消耗比较大)
 *
 * 主要是解决当 Hystrix的默认隔离策略是THREAD时,不能通过RequestContextHolder获取到request对象的问题
 *
 * Create By yxl on 2018/5/22
 */
@Component
public class FeignConfig extends HystrixConcurrencyStrategy {
    private static final Logger log = LoggerFactory.getLogger(FeignConfig.class);
    private HystrixConcurrencyStrategy delegate;

    public FeignConfig() {
        try {
            this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
            if (this.delegate instanceof FeignConfig) {
                // 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 <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 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();
            }
        }
    }
}



版权声明:本文为zhuwei_clark原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。