SpringCloud微服务调用丢失请求头的问题及解决方案

 更新时间:2024年02月20日 14:31:26   作者:晒干的老咸鱼  
在Spring Cloud 中微服务之间的调用会用到Feign,但是在默认情况下,Feign 调用远程服务存在Header请求头丢失问题,下面给大家分享SpringCloud微服务调用丢失请求头的问题及解决方案,感兴趣的朋友一起看看吧

在 Spring Cloud 中 微服务之间的调用会用到Feign,但是在默认情况下,Feign 调用远程服务存在Header请求头丢失问题。但基本上每个服务都会有一个全局globalId,能够清除调用链路,可以有两种解决方案

解决方案一

可以在每次远程调用时,使用@RequestHeader注解重新封装请求头

    @GetMapping("/test")
    String test(String res, @RequestHeader String globalId);

解决方案二

可以使用springcloud提供的feign拦截器RequestInterceptor,拦截请求头重新进行封装

package com.test.feignheader.config;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
/**
 * @Description: feign拦截器功能, 解决header丢失问题
 **/
@Configuration
public class FeignConfig {
    @Bean("requestInterceptor")
    public RequestInterceptor requestInterceptor() {
        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                //1、使用RequestContextHolder拿到刚进来的请求数据
                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                if (requestAttributes != null) {
                    //老请求
                    HttpServletRequest request = requestAttributes.getRequest();
                    if (request != null) {
                        //2、同步请求头的数据(主要是cookie)
                        //把老请求的cookie值放到新请求上来,进行一个同步
                        String cookie = request.getHeader("Cookie");
                        template.header("Cookie", cookie);
                    }
                }
            }
        };
        return requestInterceptor;
    }
}

 也可以参考下面的代码进行封装

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);
                //跳过 content-length,避免追加参数导致content-length值与实际长度不一样
                if (name.equals("content-length")) {
                   continue;
                }
                requestTemplate.header(name, values);
            }
        }
        /**  body一般不需要处理,否则会导致微服务调用异常
        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调用丢失请求头的问题就解决的了 。

到此这篇关于SpringCloud微服务调用丢失请求头的文章就介绍到这了,更多相关SpringCloud微服务调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring事务管理的使用细则浅析

    Spring事务管理的使用细则浅析

    事务的作用就是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就 回退到事务开始未进行操作的状态。事务管理是Spring框架中最为常用的功能之一,我们在使用Spring开发应用时,大部分情况下也都需要使用事务
    2023-02-02
  • SpringSecurity+jwt+redis基于数据库登录认证的实现

    SpringSecurity+jwt+redis基于数据库登录认证的实现

    本文主要介绍了SpringSecurity+jwt+redis基于数据库登录认证的实现,其中也涉及到自定义的过滤器和处理器,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • idea解决程序包不存在报错的八种解决方法

    idea解决程序包不存在报错的八种解决方法

    这篇文章主要介绍了idea解决程序包不存在报错的八种解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-02-02
  • java、js中实现无限层级的树形结构方法(类似递归)

    java、js中实现无限层级的树形结构方法(类似递归)

    下面小编就为大家带来一篇java、js中实现无限层级的树形结构方法(类似递归)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-11-11
  • java实现猜拳小游戏

    java实现猜拳小游戏

    这篇文章主要为大家详细介绍了java实现猜拳小游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • python中jieba库(中文分词库)使用安装教程

    python中jieba库(中文分词库)使用安装教程

    这篇文章主要介绍了python中jieba库(中文分词库)使用安装教程,jieba库是通过中文词库的方式来识别分词的。它首先利用一个中文词库,通过词库计算汉字之间构成词语的关联概率,所以通过计算汉字之间的概率,就可以形成分词的结果,需要的朋友可以参考下
    2023-04-04
  • mybatis定义sql语句标签之delete标签解析

    mybatis定义sql语句标签之delete标签解析

    这篇文章主要介绍了mybatis定义sql语句标签之delete标签解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java 中的弱引用是什么

    Java 中的弱引用是什么

    这篇文章主要介绍了Java 中的弱引用是什么,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-01-01
  • spring boot hutool整合email的详细过程

    spring boot hutool整合email的详细过程

    这篇文章主要介绍了spring boot hutool整合email的相关知识,本文介绍两种方式发送email文件,结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-03-03
  • Spring依赖注入和控制反转详情

    Spring依赖注入和控制反转详情

    这篇文章主要介绍了Spring依赖注入和控制反转详情,控制反转是面向对象编程中使用的术语,通过该术语,对象或对象集的控制权被赋予框架或由框架提供的容器。下文更多相关内容需要的小伙伴可以参考一下
    2022-05-05

最新评论