优化排查-线程阻塞:CompletableFuture 和 DiscardPolicy

优化排查-线程阻塞:CompletableFuture 和 DiscardPolicy

问题发现

1 前天大佬通过prometheus发现 tomcat http busy状态的线程这几天呈线性递增。每一天增加3个
![在这里插入图片描述]( https://img-blog.csdnimg.cn/9292237a25cb4c5faa82c6e5690b447e.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)

排查问题

1:找到忙线程在哪。通过jvm自带的 jps 命令可以找到服务对应的进程ID: 66182
$>$top -Hp 66182
![在这里插入图片描述]( https://img-blog.csdnimg.cn/b29ef6359f18419c88a56cfa7228893b.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)
$pidstat -u -p 66182 1 5
在这里插入图片描述

大部分的线程都正常,cpu利用率不高,而且线程ID变动快,基本排除 死循环、CPU 空转的问题

2:既然不是死循环、CPU空转。那是不是代码阻塞的问题呢。
导出 66182 进程jvm的 stack 文件 jstack -l 66182 > block66182.jstack。因为知道是http 线程的问题。http 的开头一般都是 http-nio 。可以使用 grep -A 15 'http-nio' block66182.jstack 输出一些关键信息。找了很久,多数http 都是正常状态。然后还找到了项目代码 updateXYDVerifiedCodeByDate 之类的。定位到具体,发现是一个定时任务

![在这里插入图片描述]( https://img-blog.csdnimg.cn/dec3c69057fd48368e12cdd7b99ab0d7.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)
3 查了下 xxl-task 调度日志。凌晨左右调度了十次,失败了三次,和在prometheus发现的 busy http线程增加的规律是一致的

![在这里插入图片描述]( https://img-blog.csdnimg.cn/1c6b90a182d745e7b0edd02f685f0e32.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)
4 查找代码发现是 CompletableFuture 调用get阻塞住了。后来改成 future.get(15, TimeUnit.SECONDS);。则每隔 15 秒报错一次。但是在promethues 监控到 verifiedCodeQueryExecutor 的线程队列是空的。
![在这里插入图片描述]( https://img-blog.csdnimg.cn/4d21d971df554179813c0820fb818405.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)
4.1 promethues 监控到的线程队列数为空
![在这里插入图片描述]( https://img-blog.csdnimg.cn/6623f2af8b054dcaa0306587a7d285f4.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_18,color_FFFFFF,t_70,g_se,x_16)

5 没有任务 CompletableFuture 的get方法还在执行,查看下 verifiedCodeQueryExecutor 的定义。发现,阻塞队列的拒绝策略 是 DiscardPolicy 丢弃。也就是任务丢弃了不被执行,而封装成的CompletableFuture 自然就不会有结果返回,因此一直会被阻塞,而改了代码则是超时返回。真相大白。。。。。

![在这里插入图片描述]( https://img-blog.csdnimg.cn/7897cc4b37644c6c98ce19804f9ce508.png?x-oss-process=image/watermark ,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5r2c6KGM5YmN6KGM,size_20,color_FFFFFF,t_70,g_se,x_16)

解决问题

1:队列无限,好像不太好,不接受
2:自定义拒绝策略

new RejectedExecutionHandler(){
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                throw new RuntimeException(" over size error ");
            }
        }

3:但考虑到任务必须被处理掉,任务不能被丢弃啊。so 暂时用 CallerRunsPolicy 策略,
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
4:后面再优化

posted @ 2021-09-27 11:58  潜行前行  阅读(616)  评论(0)    收藏  举报