起因:客户端带请求头请求接口–>接口开启新线程调用其他服务的接口会导致请求头丢失问题;
/**
* 伪代码
*/
public void test(){
// 自定义线程池,一般一个服务定义1~2个线程池,所有多线程执行都公用同个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
10, //核心线程数
200,//最大线程数
10, TimeUnit.SECONDS, // 非核心线程空闲时存活时间,时间单位
new LinkedBlockingDeque<>(10000), // 阻塞队列
Executors.defaultThreadFactory(), // 线程创建工厂,一般用默认
new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略,如果队列满了,按照我们指定的拒绝策略执行任务
//异步任务1
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
//调用其他服务的业务代码(会有请求头丢失问题)
}, threadPoolExecutor);
//异步任务2
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
//调用其他服务的业务代码(会有请求头丢失问题)
}, threadPoolExecutor);
// future1和future2都执行完成之后继续再执行下面的业务代码
CompletableFuture.allOf(future1,future2).get();
// 业务代码
}
原因:Feign 服务间调用请求头丢失问题,我们是加了一个拦截器去将请求头的信息重新设置到Request对象中再去请求其他服务的接口,获取ServletRequestAttributes
我们是通过RequestContextHolder.getRequestAttributes()
这个方法,从RequestContextHolder
对象中拿数据是通过ThreadLocal
,只有同个线程才能从ThreadLocal
中拿到数据,我们开启了新线程去调用其他接口,那当然是拿不到数据;
解决方案
从当前线程拿到请求头信息,调用其他服务时将请求头设置进去;
// 获取当前线程请求头信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
//异步任务1
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
//远程调用之前设置请求头
RequestContextHolder.setRequestAttributes(requestAttributes);
//调用其他服务的业务代码(会有请求头丢失问题)
}, threadPoolExecutor);
//异步任务2
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
//远程调用之前设置请求头
RequestContextHolder.setRequestAttributes(requestAttributes);
//调用其他服务的业务代码(会有请求头丢失问题)
}, threadPoolExecutor);
// future1和future2都执行完成之后继续再执行下面的业务代码
CompletableFuture.allOf(future1,future2).get();