使用场景:
做了一个大屏统计会员情况,因为业务那边想看事实情况,数据量百万级,单是统计耗死大概6 、7秒,所以就尽量做到实时20s刷新一次缓存。
伪代码:
@Scheduled
viod countMsg(){
if(RedisUtils.lock){
CountDownLatch countDownLatch = new CountDownLatch(size);
for(){
threadPoolExecutor.execute(()->{
countMethod();
countDownLatch.countDown();
});
}
//统计完成
countDownLatch.await();
System.out.println("统计时间:");
RedisUtils.releaseLock();
}
}
问题产生过程:
开始写的实时接口,写了之后发现,统计完需要7s种,然后用了多线程统计接口响应做到了3s。因为前端刷新频率还比较高,导致数据3s刷新还是有问题,就把这个代码做到缓存里面了。后面发现定时任务执行一段时间之后就没执行了,因为没有打印统计完成之后的日志
排查问题:
1.是不是任务阻塞了,但是是也没发现报错日志呢,以为是任务执行时间太长,可能是定时任务队列满了,应该也是会有报错日志的呢,而且定时任务里面也是多线程统计,并且里面也用了线程池,是不是任务里面配置的线程池满了。把任务队列调大了也没有用呢。。。无语
2.定时任务加了分布式锁,是不是产生了死锁,然后自己把锁过期时间又调整了一下,并且在finaly里面写了释放锁
3.最后发现我用了countDownLatch,如果线程没执行完,我这个任务就一直阻塞,好像找到关键的问题所在了,但是我本地跑了定时任务也没发现啥问题呀,任务也在正常执行呀。
4.最后没办法了直接连测试环境的库,跑定时任务,开始也是没有报错,执行了几十次的时候,就看到一个报错日志了,呦西!!!! 原来是countMethod里面有一个调用dubbo接口的方法没拿到数据,直接NEP了,观察了一下这个接口调用几十次之后就会出现异常,所以导致我的countDownLatch.countDown()方法没有执行,任务阻塞了。
解决方法: 当然是直接把CountDownLatch注释掉了。至于dubbo接口偶尔出现一次异常我也没管了