Feign调用时将请求头信息传递到调用下游方案

一、需求分析

  现有接口存在对用户信息进行拦截鉴权需求,在使用Feign包调用下游接口时,需要将当前请求头一直传递下去。

二、实现方案

  在参考了网上的各种资料之后,通过自定义hystrix并发策略和Feign调用拦截器实现此需求

1、拦截器定义

  拦截全部的Feign调用请求,从当前requestContext请求头中获取出需要的身份信息,再手动添加到requestTemplate中。

  这里的问题是,当请求走到此拦截器中时,如果没有配置hystrix并发策略为信号量模式的话,就会根据hystrix默认的并发策略,进入一个新的线程池。所以在这里是获取不到我们想要的requestContext的。需要通过第二步进行处理。

@Configuration
public class FeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        // 服务内部跳转时,可能不存在Request上下文
        if (Objects.isNull(attributes)){
            requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
            return;
        }
        HttpServletRequest request = attributes.getRequest();
        boolean hasParam=(request.getParameter(AuthorizationUtil.Authorization)!=null&& !"".equals(request.getParameter(AuthorizationUtil.Authorization)));
        if("GET".equals(request.getMethod())&& hasParam){
            requestTemplate.header(AuthorizationUtil.Authorization, request.getParameter(AuthorizationUtil.Authorization));
            return;
        }
        Enumeration<String> headerNames = request.getHeaderNames();
        boolean bool = false;
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                // 只取需要的header
                if ("Authorization".equals(name) || "authorization".equals(name)){
                    requestTemplate.header(name, values);
                    bool = true;
                } else if (AuthorizationUtil.Authorization.equals(name)) {
                    requestTemplate.header(name, values);
                    bool = true;
                }
            }
            if (!bool) {
                //判断url中是否有数据
                requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
            }
        } else {
            requestTemplate.header(AuthorizationUtil.Authorization, AuthorizationUtil.Authorization);
        }
    }
}

2、自定义hystrix并发策略

  重点在于继承HystrixConcurrencyStrategy之后,重写wrapCallable方法,内部把当前上下文中的信息填充到新线程的上下文中

@Configuration
public class RequestHeaderHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
    private static final Log log = LogFactory.getLog(RequestHeaderHystrixConcurrencyStrategy.class);

    private HystrixConcurrencyStrategy delegate;

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

        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();
            }
        }
    }
}

3、Feign配置注册

  如果是微服务项目,并且是通过引包的方式调用Feign接口的话,需要装配拦截器FeignInterceptor。

@Configuration
public class FeignSupportConfig {

    @Bean
    public RequestInterceptor requestInterceptor(){
        return new FeignInterceptor();
    }
}

  在项目启动类上加@ComponentScan,配置FeignSupportConfig所在的包路径,使拦截器生效。

posted @ 2023-02-23 14:29  阳光、大地和诗歌  阅读(378)  评论(0编辑  收藏  举报