使用场景:
做了一个大屏统计会员情况,因为业务那边想看事实情况,数据量不到百万,单次统计耗死大概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接口偶尔出现一次异常我也没管了。
dubbo偶尔失败的问题,会不会是我的定时任务请求得太频繁了,导致dubbo处理不过来