Feign异步模式丢失上下文问题

Feign异步模式丢失上下文问题

问题描述

当我们使用异步对我们代码进行操作优化时,代码中使用了RequestContextHolder去获取上下文的数据,当我们执行原来可以执行的业务时发现报了空指针异常或数据为空,这是为什么呢?

原理解释

通过源码我们可以看出来,RequestContextHolder本质的是使用了ThreadLocal作为上下文的实现方式,但ThreadLocal只在自己线程中才可以读取到数据,但我们开启了异步线程,导致数据在不同的线程中为空,那我们怎么解决呢?

解决方法

1、在主线程中读取出主线程的数据,通过RequestContextHolder将数据注入到子线程中即可解决此问题

java 复制代码
@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);将主线程的数据共享即可解决此问题

java 复制代码
@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;

}

复制代码
相关推荐
踏浪无痕8 小时前
手写Spring事务框架:200行代码揭开@Transactional的神秘面纱( 附完整源代码)
spring boot·spring·spring cloud
踏浪无痕8 小时前
5个测试用例带你彻底理解Spring事务传播行为( 附完整源代码)
spring boot·spring·spring cloud
刘一说9 小时前
Nacos 与 Spring Cloud Alibaba 集成详解:依赖、配置、实战与避坑指南
spring boot·spring cloud·微服务·架构
i***48619 小时前
微服务生态组件之Spring Cloud LoadBalancer详解和源码分析
java·spring cloud·微服务
兔子撩架构11 小时前
Dubbo 的同步服务调用
java·后端·spring cloud
N***H48612 小时前
SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由
java·spring cloud·gateway
0***v77712 小时前
SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则
spring cloud·gateway·sentinel
我是小妖怪,潇洒又自在14 小时前
springcloud alibaba(四)OpenFeign实现服务调用
后端·spring·spring cloud·springboot
q***38511 天前
SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由
java·spring cloud·gateway
m***66731 天前
SpringCloud Gateway 集成 Sentinel 详解 及实现动态监听Nacos规则配置实时更新流控规则
spring cloud·gateway·sentinel