当前位置: 首页 > article >正文

CompletableFuture异步业务 默认ForkJoinPool 导致类加载器加载类失败

目录

1、Bug案发现场

2、捉虫过程过程

3、解决方案与代码

4、成果展现与总结 ​编辑

5、参考文章


1、Bug案发现场

        最近参与帮助以前同事实际业务开发中业务,在一个业务场景之中;使用H5页面通过二维码收集小微企业/个体工商户贷款业务需求。其中在获得用户输入企业名称后,获得企业或者个体工商户信用代码后,使用CompletableFuture的.supplyAsync方法实现异步保存企业基本信息到数据库之中;但是此异步操作类默认使用的ForkJoinPool 作为线程池。于是发生一个奇怪的Bug,在测试环境(test)【阿里云的ECS Docker JDK21+MySQL8.0.36+Redis 7.0.5】可以很正常实现数据保存;但是在正式环境部署到山东某市客户服务器上【Docker JDK21+Redis 7.0.5+MySQL5.7.44】死活无法保存基本公司信息到数据库里面。

具体出错问题就是下面这行代码

redisTemplate.boundValueOps(CACHE_KEY_PREFIX + key).get()

非法参数异常,未找到类对应的类。具体错误信息如下所示 :

+---[39.81% 0.872861ms ] vip.xiaonuo.common.cache.CommonCacheOperator:get() #203 [throws Exception]
        `---throw:java.lang.IllegalArgumentException #881 [org.springframework.data.redis.core.BoundValueOperations referenced from a method is not visible from class loader: 'app']  

2、捉虫过程过程

       我们在这个过程之中,怀疑过JDK21版本问题,退回到JDK17;怀疑过Redis问题也搭建相同的版本;所有的验证都是徒劳的。并且比较奇怪的Test环境一切正常。生产环境就是报错。从业务角度来说也是需要使用异步的方式来分开处理业务需求。甚至网上找到github上一篇类似文章。

https://github.com/spring-projects/spring-data-redis/issues/2772 【国内可能无法打开 上有截图】

        最后思考后继续寻找异常关键词语

throw:java.lang.IllegalArgumentException #881 [org.springframework.data.redis.core.BoundValueOperations referenced from a method is not visible from class loader

        于是SegmentFault 思否 网站上找一篇类似文章,有相关异常描述。最后定位并确定就是因为以前使用 CompletableFuture.supplyAsync() 异步方法,默认使用的 ForkJoinPool 作为线程池所导致的。

       在有上述思路后,今天去开发现场,使用自己定义线程池;替换了默认ForkJoinPool线程池;时间这个困扰1周左右的问题。并且实现了基本公司信息正确正常入库。

3、解决方案与代码

    自定义线程池

@Configuration
public class ThreadPoolConfig {
    // 核心线程数
    private int corePoolSize = 5;
    // 最大线程数
    private int maxPoolSize = 10;
    // 队列大小
    private int queueSize = 40;
    // 线程最大空闲时间
    private int keepAliveSeconds = 40;
    /**
     * 自定义消费队列线程池
     * CallerRunsPolicy 这个策略重试添加当前的任务,他会自动重复调用 execute() 方法,直到成功。
     * AbortPolicy 对拒绝任务抛弃处理,并且抛出异常。
     * DiscardPolicy 对拒绝任务直接无声抛弃,没有异常信息。
     * DiscardOldestPolicy 对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。
     * @return
     */

    @Bean(value = "customAsyncTaskThreadPool")
    public ThreadPoolTaskExecutor buildCustomAsyncTaskThreadPool() {
        ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
        threadPool.setCorePoolSize(corePoolSize);
        threadPool.setMaxPoolSize(maxPoolSize);
        threadPool.setQueueCapacity(queueSize);
        threadPool.setThreadNamePrefix("customAsyncTaskThreadPool-");
        threadPool.setKeepAliveSeconds(keepAliveSeconds);
        threadPool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //如果@Bean 就不需手动,会自动InitializingBean的afterPropertiesSet来调initialize
        //threadPool.initialize();
        return threadPool;
    }
@Resource
@Qualifier("customAsyncTaskThreadPool")
private Executor asyncTaskThreadPoolExecutor;


private void saveCompanyBasicInfo(CompanyLoanDemandCreateParam companyLoanDemandCreateParam) {
        CompletableFuture.supplyAsync(() -> {
            try {
                String uscc = companyLoanDemandCreateParam.getUscc();
                String key = String.format(BusinessConstant.WE_COMPANY_CACHE_KEY, uscc);
                log.error("key  "+key);
                Object data =null;
                try {
                     data =commonCacheOperator.get(key);
                } catch (Exception e) {
                    log.error(e.toString());
                }
                if (ObjectUtils.isEmpty(data)) {
                    //处理业务内容
                }
            } catch (ParseException e) {
                log.error("saveCompanyBasicInfo error", e);
            }
            return null;
        },asyncTaskThreadPoolExecutor);

4、成果展现与总结 

        通过解决困扰我同事与我这个问题;让我明白一个道理,如同最近在看《毛选--->实践论与矛盾论》要解决实际问题只有深入实践并且注意矛盾的特殊性;因为每个开发场景可能与网上文章有相似【矛盾的普遍性】但是又有其自己开发与运行环境的【特殊性】;只有抓住特殊性并结合普遍性才能够找到解决问题的主要矛盾;并使得获得有效的方法论去解决实际问题。

5、参考文章

      ForkJoinPool 导致类加载器加载类失败

     springboot自定义线程池,CompletableFuture实现多线程并发

    生产问题之CompletableFuture默认线程池踩坑,请务必自定义线程池

    CompletableFuture使用自定义线程池实现多任务结果聚合返回 


http://www.kler.cn/a/440729.html

相关文章:

  • 起售停售套餐
  • tij15_泛型
  • Java性能调优 - JVM性能监测及调优
  • React Native学习路线图
  • 计算机组成原理(八):加法器
  • ps案例制作
  • centos使用mkisofs构建无人值守镜像(附官方学习文档)
  • python webdriver-manager 实现selenium 免下载安装webdriver
  • 51c视觉~合集31
  • 【C++】红黑树(万字)
  • 【AIStarter】3.2.1版本更新:告别Bug,提升用户体验
  • 前端(七)定位流
  • 单片机:实现utf-8转gb2312(附带源码)
  • 银河麒麟桌面操作系统添加WPS字体
  • 如何在 Ubuntu 上安装 OpenSearch 开源的搜索引擎
  • BGP-面试
  • Zookeeper其二,zk的java和选举机制,Hadoop的高可用和联邦机制
  • c++ multimap
  • TCP小队列与WiFi聚合
  • 计算机网络 | 5.传输层