多线程/异步线程,新开启的线程获取不到原request

1,044 阅读1分钟

背景描述:

邮件发送功能,通过异步线程去执行邮件发送,发送过程中需要通过远程调用人员服务来获取收件人账号对应的邮件地址,远程调用会有个拦截器去根据请求的reques传递token信息,这时候会发现从上下文获取RequestAttributes为null.

@Component
public class FeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        HttpServletRequest request = getServletRequest();
        if (null == request){
            return;
        }
        template.header("token", getServletRequest().getHeader("token"));
        template.header("systemId", getServletRequest().getHeader("systemId"));
    }
    private HttpServletRequest getServletRequest() {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return ((ServletRequestAttributes) requestAttributes).getRequest();
    }
}

原因分析:

RequestContextHolder获取request,是基于ThreadLocal存储的信息,ThreadLocal类似于map一样存储key,value来存储requestAttributes信息,只不过它的key是当前运行线程。Springboot默认使用ThreadLocal把request设置到请求线程中,这样如果在请求方法里面另起一个子线程后再通过getRequestAttributes方法获取,是获取不到的。

解决方法:

所以要能让子线程能够获取到requestAttributes,可以使用InherbritableThreadlocal,可以通过

RequestContextHolder.setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable)

来设置子线程共享attributes。
在子线程执行前执行:
异步调用,设置子线程共享

ServletRequestAttributes servletRequestAttributes = 
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
RequestContextHolder.setRequestAttributes(servletRequestAttributes,true);
// 先获取requestAttributes
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ExecutorService executor = ExecutorUtil.newFixedThreadPool(1, "send-message-%d");
executor.execute(() -> {
  // 设置
  RequestContextHolder.setRequestAttributes(requestAttributes);
   })