• spring cloud微服务中多线程下,子线程通过feign调用其它服务,请求头token等丢失


    在线程池中,子线程调用其他服务,请求头丢失,token为空的情况

    看了很多篇文章的处理方法和在自己亲测的情况下做出说明:

    第一种:

    在这里插入图片描述
    这种方式只支持在主线程情况下,能够处理,在多线程情况下,一旦主线程结束,这里还是会为空

    第二种

    //请求属性可继承,线程共享
    RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(),true);
    
    • 1
    • 2

    这种经测试后发现,主线程直接启动子线程,且执行完自己逻辑后便结束不需理会子线程结果的,请求偶尔成功, 偶尔失败;

    也就是,当父线程比子线程执行完慢时,请求属性还在,子线程请求成功;当快时,请求属性随着父线程结束而销毁,子线程的请求属性变为null,请求失败。

    第三种

    采用的处理方式为:ThreadLocal

    新建一个ThreadLocal 工具类,在多线程请求前,获取到需要的属性值或者设置所有的属性值放入工具类MAP种进行存储,在子线程调用服务时通过监听处将需要的值取出,就可以解决了。实际如下:

    public class ThreadLocalUtil {
    
        //使用InheritableThreadLocal,使得共享变量可被子线程继承
        private static final InheritableThreadLocal> headerMap = new InheritableThreadLocal>(){
            @Override
            protected Map, String> initialValue() {
                return new HashMap<>();
            }
        };
    
        public static Map get(){
            return headerMap.get();
        }
    
        public static String get(String key) {
            return headerMap.get().get(key);
        }
    
        public static void set(String key, String value){
            headerMap.get().put(key,value);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在线程执行前加:
    (1

    		Enumeration headerNames = servletRequest.getHeaderNames();
            while (headerNames.hasMoreElements()){
                String name = headerNames.nextElement();
                if (Objects.equals(name,"feignheader")){
                    ThreadLocalUtil.set(name,servletRequest.getHeader(name));
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    或者直接获取token,在需要的地方再进行赋值。
    (2

    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    ServletRequestAttributes srat = (ServletRequestAttributes) requestAttributes;
    HttpServletRequest request = srat.getRequest();
    ThreadLocalUtil.set("token", request.getHeader("authorization"));
    
    • 1
    • 2
    • 3
    • 4

    修改监听处获取请求头信息赋值

    (1

    @Slf4j
    @Configuration
    public class FeignConfig implements RequestInterceptor {
    
        @Override
        public void apply(RequestTemplate requestTemplate) {
    //        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    //        //当主线程的请求执行完毕后,Servlet会被销毁,因此在这里需要做判空
    //        if (attributes != null) {
    //            HttpServletRequest request = attributes.getRequest();
    //
    //            Enumeration headerNames = request.getHeaderNames();
    //
    //            while (headerNames.hasMoreElements()) {
    //                String name = headerNames.nextElement();
    //                //不能把所有消息头都传递下去,否则会引起其他异常;header的name都是小写
    //                if (name.equals("feignheader")) {
    //                    requestTemplate.header(name,request.getHeader(name));
    //                }
    //            }
    //        }
    
            //读取设置的header信息,传递到下一个服务
            Map, String> headerMap = ThreadLocalUtil.get();
            for (String key : headerMap.keySet()) {
                log.info("--从ThreadLocal获取消息头传递到下一个服务:key-[{}],value-[{}]",key,headerMap.get(key));
    
                requestTemplate.header(key,headerMap.get(key));
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    (2

    这里之所以直接拿token,是因为后面传递获取token,未获取到的问题,如果有其它信息丢失,可用上面(1 的方法,会更全面一点

    @Slf4j
    @Configuration
    public class FeignConfig implements RequestInterceptor {
    
        @Override
        public void apply(RequestTemplate requestTemplate) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            String token = null;
            //当主线程的请求执行完毕后,Servlet会被销毁,因此在这里需要做判空
            if (attributes != null) {
    			ServletRequestAttributes srat = (ServletRequestAttributes) requestAttributes;
    			HttpServletRequest request = srat.getRequest();
    			token = request.getHeader("authorization");
    		}
    		token =  StringUtils.isNotBlank(token) ? token : ThreadLocalUtil.get("token");
    		//将token传递出去
            requestTemplate.header("authorization", token);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    Qt5开发及实例V2.0-第一章Qt概述及Qt5的安装
    DispatcherServlet工作原理简介说明
    ES6之Map和Set有什么不同?
    Gauva的ListenableFuture
    常见的七大排序
    蓝桥杯实战应用【算法代码篇】-二分查找的递归解法(附python、Java、C语言、R语言和C++代码)
    Java学习Day017(第三章:流程控制—选择结构与循环结构笔记)
    图像检测:医疗影像分割
    软考知识点2
    Java服务总在半夜挂,背后的真相竟然是...
  • 原文地址:https://blog.csdn.net/Mou_O/article/details/134402143