Feign异步模式丢失上下文问题
问题描述
当我们使用异步对我们代码进行操作优化时,代码中使用了RequestContextHolder去获取上下文的数据,当我们执行原来可以执行的业务时发现报了空指针异常或数据为空,这是为什么呢?
原理解释
通过源码我们可以看出来,RequestContextHolder本质的是使用了ThreadLocal作为上下文的实现方式,但ThreadLocal只在自己线程中才可以读取到数据,但我们开启了异步线程,导致数据在不同的线程中为空,那我们怎么解决呢?
解决方法
1、在主线程中读取出主线程的数据,通过RequestContextHolder将数据注入到子线程中即可解决此问题
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = new OrderConfirmVo();
MemberLoginTo memberLoginTo = loginToThreadLocal.get();
System.out.println("主线程的id:"+Thread.currentThread().getId());
// 主线程的threadLocal数据,注意threadLocal中的数据只是在本线程中生效,若启用异步线程则会出现线程读取不到数据的问题
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 1、远程查询所有的收货地址的列表
CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
// 将主线程的threadLocal共享到子线程,避免出现读取不到子线程上下文数据的问题
System.out.println("当前线程的id:"+Thread.currentThread().getId());
RequestContextHolder.setRequestAttributes(requestAttributes);
List<MemberAddressVo> address = memberFeignService.getAddress(memberLoginTo.getId());
confirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> CartItemFuture = CompletableFuture.runAsync(() -> {
// 2、远程查询购物车的购物项列表
// 将主线程的threadLocal共享到子线程,避免出现读取不到子线程上下文数据的问题
System.out.println("当前线程的id:"+Thread.currentThread().getId());
RequestContextHolder.setRequestAttributes(requestAttributes);
List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
confirmVo.setOrderItems(userCartItems);
// feign 在远程调用之前要构造请求,会调用很多的拦截器
}, executor);
return confirmVo;
}
2、在主线程中使用RequestContextHolder.setRequestAttributes(requestAttributes,true);
将主线程的数据共享即可解决此问题
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = new OrderConfirmVo();
MemberLoginTo memberLoginTo = loginToThreadLocal.get();
System.out.println("主线程的id:"+Thread.currentThread().getId());
// 主线程的threadLocal数据,注意threadLocal中的数据只是在本线程中生效,若启用异步线程则会出现线程读取不到数据的问题
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
// 开启线程数据共享
RequestContextHolder.setRequestAttributes(requestAttributes,true);
// 1、远程查询所有的收货地址的列表
CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
// 将主线程的threadLocal共享到子线程,避免出现读取不到子线程上下文数据的问题
System.out.println("当前线程的id:"+Thread.currentThread().getId());
// RequestContextHolder.setRequestAttributes(requestAttributes);
List<MemberAddressVo> address = memberFeignService.getAddress(memberLoginTo.getId());
confirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> CartItemFuture = CompletableFuture.runAsync(() -> {
// 2、远程查询购物车的购物项列表
// 将主线程的threadLocal共享到子线程,避免出现读取不到子线程上下文数据的问题
System.out.println("当前线程的id:"+Thread.currentThread().getId());
// RequestContextHolder.setRequestAttributes(requestAttributes);
List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
confirmVo.setOrderItems(userCartItems);
// feign 在远程调用之前要构造请求,会调用很多的拦截器
}, executor);
return confirmVo;
}
要构造请求,会调用很多的拦截器
}, executor);
return confirmVo;
}