异步子线程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接口将特定请求头放进去。而主线程本身含有这些,所以不需要你去添加。
结语
关于这方面其实网上有很多的解决方案,但是很多都讲的不是详细,大多是找到解决方案之后就过,对于失败方案并没有一个解释,让人阅读起来很难受。