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

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

第一种:

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

第二种

//请求属性可继承,线程共享

RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(),true);

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

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

第三种

采用的处理方式为:ThreadLocal

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

public class ThreadLocalUtil {

//使用InheritableThreadLocal,使得共享变量可被子线程继承

private static final InheritableThreadLocal> headerMap = new InheritableThreadLocal>(){

@Override

protected Map 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

Enumeration headerNames = servletRequest.getHeaderNames();

while (headerNames.hasMoreElements()){

String name = headerNames.nextElement();

if (Objects.equals(name,"feignheader")){

ThreadLocalUtil.set(name,servletRequest.getHeader(name));

}

}

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

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

ServletRequestAttributes srat = (ServletRequestAttributes) requestAttributes;

HttpServletRequest request = srat.getRequest();

ThreadLocalUtil.set("token", request.getHeader("authorization"));

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

(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 headerMap = ThreadLocalUtil.get();

for (String key : headerMap.keySet()) {

log.info("--从ThreadLocal获取消息头传递到下一个服务:key-[{}],value-[{}]",key,headerMap.get(key));

requestTemplate.header(key,headerMap.get(key));

}

}

}

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

}

}

后面看到这篇文章讲的 很详细:SpringCloud OpenFeign 服务调用传递 token

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: