如何解决异步任务上下文丢失问题

 更新时间:2024年09月18日 10:48:18   作者:勿语&  
在多线程编程中,异步任务可能会导致上下文信息丢失,为了解决这个问题,可以在执行异步任务前,通过自定义TaskDecorator拷贝主线程的上下文至子线程,这样可以确保上下文在异步执行过程中得以保留,将定制的TaskDecorator设置至线程池,可以有效地解决上下文丢失问题

解决异步任务上下文丢失问题

  • 上下文丢失主要是因为主线程和子线程的上下文不能共享。
  • 可以通过执行异步任务之前,将主线程上的上下文信息拷贝到子线程上。

自定义TaskDecorator 来拷贝主线程上的上下文信息到子线程,然后将自定义的 TaskDecorator实现类 设置到线程池上。

@Configuration
public class ThreadPoolConfig {

    @Bean(name = "customizeTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor poolExecutor = new ThreadPoolTaskExecutor();
        // 核心线程数=cpu核心数+1
        poolExecutor.setCorePoolSize(5);
        // 最大线程数=cpu核心数*2
        poolExecutor.setMaxPoolSize(8);
        // 设置任务装饰器
        poolExecutor.setTaskDecorator(taskDecorator());
        // 任务被拒绝后,交给调用线程执行
        poolExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return poolExecutor;
    }

    @Bean
    public TaskDecorator taskDecorator(){
        return new TaskDecorator() {
            @Override
            public Runnable decorate(Runnable runnable) {
                RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                return ()->{
                    try {
                        RequestContextHolder.setRequestAttributes(requestAttributes);
                        runnable.run();
                    }finally {
                        RequestContextHolder.resetRequestAttributes();
                    }
                };
            }
        };
    }
}

Fegin异步情况丢失上下文问题

在微服务的开发中,我们经常需要服务之间的调用,并且为了提高效率使用异步的方式进行服务之间的调用,在这种异步的调用情况下会有一个严重的问题,丢失上文下

通过以上图片可以看出异步丢失上下文的原因是不在同一个线程,所有数据不能共享,Wie了解决这个问题,我们就需要把之前线程的请求头上下文,在次存放到其他线程的请求头上下文就行,具体实现如下:

案例:feign异步获取订单明细的案例代码

/**
     * 获取订单明细的vo
     * @return
     */
    @Override
    public OrderConfirmVo orderConfirm() {
        MemberResponseVo member = OrderInterceptor.threadLocal.get();
 
        OrderConfirmVo orderConfirmVo = new OrderConfirmVo();
        System.out.println("主线程:"+ Thread.currentThread().getId());
        //获取主线程的请求头信息
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
 
        //考虑到效率问题  使用异步编排
        CompletableFuture<Void> getAddress = CompletableFuture.runAsync(() -> {
            //子线程中设置添加主线程的请求头信息  信息共享  否则远程调用异步处理丢失请求头信息
            RequestContextHolder.setRequestAttributes(requestAttributes);
            System.out.println("address:"+ Thread.currentThread().getId());
            //远程获取地址信息
            List<MemberAddressVo> address = memberFeignService.getAddress(member.getId());
            orderConfirmVo.setAddress(address);
        }, executor);
 
 
        CompletableFuture<Void> getItem = CompletableFuture.runAsync(() -> {
            //子线程中设置添加主线程的请求头信息  信息共享  否则远程调用异步处理丢失请求头信息
            RequestContextHolder.setRequestAttributes(requestAttributes);
            System.out.println("item:"+ Thread.currentThread().getId());
            //远程获取购物项
            List<OrderItemVo> currentUserCartItems = cartFeignService.getCurrentUserCartItems();
            orderConfirmVo.setItems(currentUserCartItems);
        }, executor).thenRunAsync(()->{
            List<OrderItemVo> items = orderConfirmVo.getItems();
            //获取所有商品的id
            List<String> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
            List<Long> skuIds = collect.stream().map(item -> {
                return Long.parseLong(item);
            }).collect(Collectors.toList());
            R<List<SkuHasStockVo>> skusHasStock = wmsFeignService.getSkusHasStock(skuIds);
            List<SkuHasStockVo> data = skusHasStock.getData(new TypeReference<List<SkuHasStockVo>>() {
            });
            if(data!= null){
                Map<Long, Boolean> collect1 = data.stream().collect(Collectors.toMap(SkuHasStockVo::getSkuId, SkuHasStockVo::getHasStock));
                orderConfirmVo.setStocks(collect1);
            }
        },executor);
 
        //异步编排完成之后执行后续操作
        try {
            CompletableFuture.allOf(getAddress,getItem).get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
 
 
        orderConfirmVo.setIntegration(member.getIntegration());
 
        orderConfirmVo.setPayPrice(orderConfirmVo.getPayPrice());
        orderConfirmVo.setTotal(orderConfirmVo.getTotal());
        //TODO 放重处理  生成token令牌储存在redis
        String token = UUID.randomUUID().toString().replace("-", "");
        orderConfirmVo.setOrderToken(token);
        redisTemplate.opsForValue().set(OrderConstant.ORDER_TOKEN+member.getId(),token);
 
        return orderConfirmVo;
    }

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java类变量和成员变量初始化过程的应用介绍

    Java类变量和成员变量初始化过程的应用介绍

    昨天看了一本叫做《突破程序员基本功的16课》的书,个人感觉还可以,主要对Java的技巧进行了一些深入的讲解,让我对类的初始化和对象的创建有了新的认识
    2013-04-04
  • 用Maven插件生成Mybatis代码的实现方法

    用Maven插件生成Mybatis代码的实现方法

    本文主要介绍 Maven插件生成Mybatis代码,现在做开发的朋友有好多用Maven 来管理代码,这里给大家举个例子,有需要的同学可以看下
    2016-07-07
  • 详解Mybatis中常用的约束文件

    详解Mybatis中常用的约束文件

    这篇文章主要介绍了详解Mybatis中常用的约束文件,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-09-09
  • 基于Java实现音乐播放器的示例代码

    基于Java实现音乐播放器的示例代码

    这篇文章主要为大家详细介绍了如何利用Java编写一个简单的音乐播放器,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
    2023-07-07
  • Java数据结构之数组(动力节点之Java学院整理)

    Java数据结构之数组(动力节点之Java学院整理)

    这篇文章主要介绍了Java数据结构之数组(动力节点之Java学院整理)的相关资料,包括创建和内存分配,数组封装后的使用等,需要的朋友参考下吧
    2017-04-04
  • 详解如何在SpringBoot项目中使用统一返回结果

    详解如何在SpringBoot项目中使用统一返回结果

    在一个完整的项目中,如果每一个控制器的方法都返回不同的结果,那么对项目的维护和扩展都会很麻烦。因此,本文为大家准备了SpringBoot项目中使用统一返回结果的方法,需要的可以参考一下
    2022-10-10
  • Java中数据库常用的两把锁之乐观锁和悲观锁

    Java中数据库常用的两把锁之乐观锁和悲观锁

    这篇文章主要介绍了数据库常用的两把锁之乐观锁和悲观锁,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • SpringMvc中的Bean加载机制详解

    SpringMvc中的Bean加载机制详解

    这篇文章主要介绍了SpringMvc中的Bean加载机制详解,在Spring MVC中,Bean的作用主要是处理应用程序的业务逻辑和数据,例如,一个用户管理应用程序的Bean可能包括UserService、UserDao和UserController等,需要的朋友可以参考下
    2023-12-12
  • Java中绝对值函数的介绍与其妙用

    Java中绝对值函数的介绍与其妙用

    这篇文章主要给大家介绍了Java中绝对值函数的介绍与其妙用,其中包括绝对值函数用来获取表达式的绝对值和绝对值函数实现降序+升序输出。文章末尾给出了实例介绍,有需要的朋友们可以参考学习,下面来一起看看吧。
    2017-01-01
  • Spring中的10种事务失效的常见场景

    Spring中的10种事务失效的常见场景

    这篇文章主要介绍了Spring中的10种事务失效的常见场景,Spring的声明式事务功能更是提供了极其方便的事务配置方式,配合Spring Boot的自动配置,大多数Spring Boot项目只需要在方法上标记@Transactional注解,即可一键开启方法的事务性配置,需要的朋友可以参考下
    2023-11-11

最新评论