记一次多线程并发问题的排查

13 篇文章 9 订阅

背景

最近在做离线batch任务执行的中间件,目标将线上所有的批任务都接过来,以便Hive向Spark
迁移,对任务整个链路追踪(从开始预执行,到执行引擎选择,到执行日志收集,到执行完成后结果分析 是否倾斜等等)。
在做自适应选择执行引擎的时候,定义了一个proposer,里面写了一些规则,来决定使用Spark还是Hive。

现象

在大量并发提交任务时,会出先proposer不生效的情况,应该用Hive执行的SQL却用了Spark。

问题代码

masterSchedule.scheduleAtFixedRate(() -> {
            while (scheduleQueue.size() > 0) { // 存放任务的优先阻塞队列中有任务
                try {
                    JobHistory tmpJob = scheduleQueue.take(); // 拿个出队列
                    if (proposer.preHandler(tmpJob.getSqlAll()); != 0) { // 通过判断该任务的SQL,来确定用Hive还是Spark执行
                        // 用Hive执行
                        continue;
                    }
                    // 不用Hive就把任务放回去,准备用Spark执行
                    scheduleQueue.put(tmpJob);

					// 先获取一个Spark ThrifServer连接,然后直接丢给执行线程池执行Spark任务
                    executeJobPool.execute(() -> { 
                        // 用Spark执行
                        JobHistory jobHistory = null;
                        try {
                            // 任务出队列
                            jobHistory = scheduleQueue.take();
                        } catch (InterruptedException e) {
                            ...
                        }
                        // 省略Spark执行逻辑
                    });
                } catch (SQLException e) {
                    ...
                }
            }
        }, 5, 5, TimeUnit.SECONDS);

问题所在

用了2个线程池,一个是定时扫描任务队列的线程池,一个是Spark任务执行的线程池。
所有的用到的组件都是线程安全的,那么只可能是代码逻辑上非线程安全。
发现,在两个线程池调用之间,任务队列scheduleQueue被公用了,这就是问题的关键。
仔细一想:由于是优先阻塞队列,本来判断好应该用Spark执行的job,由于优先级put回队首后,还没被下面take出来,就已经被别的线程执行proposer时,把该job从队首take出来了,从而导致了Spark这边take出来的不是刚刚put到队首的那个任务了。

改进方法

之前逻辑是先判断用Hive还是Spark,如果是Spark,再去向ThriftServer连接池拿连接。
改进后先拿Spark连接,拿到了连接再去判断用Hive还是Spark,如果是Spark则直接执行,避免了put回去,如果是Hive则用Hive逻辑执行。但这样会导致Spark的负载成为了执行Hive任务的瓶颈,已经能满足目前业务需求,计划之后再进行优化。

总结

在存在多个线程池嵌套使用时,一定要多多关注只被一个线程池覆盖的代码是否线程安全或是否会引发多线程不一致的问题(本例子就是代码线程安全但逻辑非线程安全)。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值