异步子线程Feign调用增加请求头

1.前言

项目开发的时候遇到一种情况,为了减少接口时间,需要使用异步将计算结果部分放入子线程中自行运行,主线程不必一直等待计算结果,同时项目中使用请求头进行数据隔离。

2.问题

因为项目中有拦截器的存在,在接口调用的时候必须要增加请求头,最开始直接在子线程中增加了请求头,本服务的DAO层调用没有问题,但是当进行feign接口调用时,提示请求头不存在。

3.解决方案

方案1

最开始同事提供了一种方案:取出主线程中的Attributes再放入子线程中,代码如下:

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

在子线程中set进去:

RequestContextHolder.setRequestAttributes(requestAttributes);

这种方案对我并没有生效,依然存在请求头问题,但是同事用这个方案解决了他异步调用的问题,稍后会解释具体原因。

方案2

通过在网上查询feign设置header得知,有以下五种方案:

  • 在@RequestMapping注解里添加headers属性
  • 在方法参数前面添加@RequestHeader注解
  • 在方法或者类上添加@Headers的注解
  • 在方法参数前面添加@HeaderMap注解
  • 实现RequestInterceptor接口

但是由于使用的低代码开发平台,前四种对于我并不适用,所以只能选用第五种方式:实现RequestInterceptor接口。代码如下:

@Component
public class FeignRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        //判断是否是主线程
        if (ObjectUtil.isNotEmpty(RequestContextHolder.getRequestAttributes())){
            return;
        }
		//将当前线程的请求头注入到request中
        template.header("x-winplus-factory-code", WinRequestUtil.getCurrentFactory());
    }
}

使用该方法需要注意的是:这里是对全局所有的feign接口调用都生效的,由于只需要对子线程进行修改,如果Attributes存在则说明是主线程直接返回不修改主线程的数据。同时进入子线程的第一步需要先注入子线程的请求头,一方面当前服务的DAO执行需要请求头进行数据隔离,另一方面上面代码获取的是当前线程的请求头数据。

方案三

究极偷懒的方案,那就是将feign接口的调用提到主线程中使用,再将获取到的数据入参到子线程中,这样你可以不需要进行任何修改,这也是我当时实在找不到解决方法后所使用的

4.解疑

问题一:为什么有时候注入Attributes可以解决问题

这里其实要分两种情况:异步是否是阻塞性,即:你的主线程是否需要等待子线程的结果。如果你的主线程需要等子线程出结果之后才会继续往下进行,那么你可以使用这种方案,如果主线程不需要等待子线程的结果,那么这种方案就是无效的。

原因

因为你这里使用的是主线程的Attributes,当你的主线程不需要等待子线程的结果的时候,主线程可能会在子线程之前跑完,而主线程跑完之后会清空Attributes,这时你的子线程去获取使用的Attributes就会是一个空值,这时去调用feign接口必然会报无请求头的错误。但是如果你的主线程需要等待子线程的结果出来后才会进行下一步,那么在子线程运行的过程中主线程的Attributes是一直存在的,子线程去使用主线程的Attributes不会出现问题。即如果异步是阻塞性的,可以直接使用注入Attributes解决问题

问题二:为什么子线程中注入了请求头,当时调用feign接口的时候,仍提示没有请求头

原因:

在子线程中即使注入了请求头,但是由于子线程去调用feign接口的时候是没有request的,换种说法:子线程在调用feign的时候重新包装的request是不会将你线程中注入的请求头包装进去的,所以需要你去实现RequestInterceptor接口将特定请求头放进去。而主线程本身含有这些,所以不需要你去添加。

结语

关于这方面其实网上有很多的解决方案,但是很多都讲的不是详细,大多是找到解决方案之后就过,对于失败方案并没有一个解释,让人阅读起来很难受。