内容简介:本文是 Arthas 系列文章的第二篇。Dubbo 线程池满异常应该是大多数 Dubbo 用户都遇到过的一个问题,本文以 Arthas 3.1.7 版本为例,介绍如何针对该异常进行诊断,主要使用到理解线程池满异常需要首先了解 Dubbo 线程模型,官方文档:
本文是 Arthas 系列文章的第二篇。
Dubbo 线程池满异常应该是大多数 Dubbo 用户都遇到过的一个问题,本文以 Arthas 3.1.7 版本为例,介绍如何针对该异常进行诊断,主要使用到 dashboard / thread 两个指令。
Dubbo 线程池满异常介绍
理解线程池满异常需要首先了解 Dubbo 线程模型,官方文档: http://dubbo.apache.org/zh-cn/docs/user/demos/thread-model.html。简单概括下 Dubbo 默认的线程模型:Dubbo 服务端每次接收到一个 Dubbo 请求,便交给一个线程池处理,该线程池默认有 200 个线程,如果 200 个线程都不处于空闲状态,则
客户端会报出如下异常:
Caused by: java.util.concurrent.ExecutionException: org.apache.dubbo.remoting.RemotingException: Server side(192.168.1.101,20880) threadpool is exhausted ...
服务端会打印 WARN 级别的日志:
[DUBBO] Thread pool is EXHAUSTED!
引发该异常的原因主要有以下几点:
- 客户端/服务端超时时间设置不合理,导致请求无限等待,耗尽了线程数
- 客户端请求量过大,服务端无法及时处理,耗尽了线程数
- 服务端由于 fullgc 等原因导致处理请求较慢,耗尽了线程数
- 服务端由于数据库、 Redis 、网络 IO 阻塞问题,耗尽了线程数
原因可能很多,但纠其根本,都是因为业务上出了问题,导致 Dubbo 线程池资源耗尽了。所以出现该问题,首先要做的是:
- 排查业务异常
紧接着针对自己的业务场景对 Dubbo 进行调优:
- 调整 Provider 端的 dubbo.provider.threads 参数大小,默认 200,可以适当提高。多大算合适?至少 700 不算大;不建议调的太小,容易出现上述问题
- 调整 Consumer 端的 dubbo.consumer.actives 参数,控制消费者调用的速率。这个实践中很少使用,仅仅一提
- 客户端限流
- 服务端扩容
- Dubbo 目前不支持给某个 service 单独配置一个隔离的线程池,用于保护服务,可能在以后的版本中会增加这个特性
另外,不止 Dubbo 如此设计线程模型,绝大多数服务治理框架、 HTTP 服务器都有业务线程池的概念,所以理论上它们都会有线程池满异常的可能,解决方案也类似。
那竟然问题都解释清楚了,我们还需要排查什么呢?一般在线上,有很多运行中的服务,这些服务都是共享一个 Dubbo 服务端线程池,可能因为某个服务的问题,导致整个应用被拖垮,所以需要排查是不是集中出现在某个服务上,再针对排查这个服务的业务逻辑;需要定位到线程堆栈,揪出导致线程池满的元凶。
定位该问题,我的习惯一般是使用 Arthas 的 dashboard 和 thread 命令,而在介绍这两个命令之前,我们先人为的构造一个 Dubbo 线程池满异常的例子。
复现 Dubbo 线程池满异常
配置服务端线程池大小
dubbo.protocol.threads=10
默认大小是 200,不利于重现该异常
模拟服务端阻塞
@Service(version = "1.0.0")
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
sleep();
return "Hello " + name;
}
private void sleep() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
sleep 方法模拟了一个耗时操作,主要是为了让服务端线程池耗尽。
客户端多线程访问
for (int i = 0; i < 20; i++) {
new Thread(() -> {
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
demoService.sayHello("Provider");
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
问题复现
客户端
服务端
问题得以复现,保留该现场,并假设我们并不知晓 sleep 的耗时逻辑,使用 Arthas 来进行排查。
dashboard 命令介绍
$ dashboard
执行效果
可以看到如上所示的面板,显示了一些系统的运行信息,这里主要关注 THREAD 面板,介绍一下各列的含义:
分:秒
在空闲状态下线程应该是处于 WAITING 状态,而因为 sleep 的缘故,现在所有的线程均处于 TIME_WAITING 状态,导致后来的请求被处理时,抛出了线程池满的异常。
在实际排查中,需要抽查一定数量的 Dubbo 线程,记录他们的线程编号,看看它们到底在处理什么服务请求。使用如下命令可以根据线程池名筛选出 Dubbo 服务端线程:
dashboard | grep "DubboServerHandler"
thread 命令介绍
使用 dashboard 筛选出个别线程 id 后,它的使命就完成了,剩下的操作交给 thread 命令来完成。其实, dashboard 中的 thread 模块,就是整合了 thread 命令,但是 dashboard 还可以观察内存和 GC 状态,视角更加全面,所以我个人建议,在排查问题时,先使用 dashboard 纵观全局信息。
thread 使用示例:
-
查看当前最忙的前 n 个线程
$ thread -n 3
-
显示所有线程信息
$ thread
和
dashboard中显示一致 -
显示当前阻塞其他线程的线程
$ thread -b No most blocking thread found! Affect(row-cnt:0) cost in 22 ms.
这个命令还有待完善,目前只支持找出 synchronized 关键字阻塞住的线程, 如果是
java.util.concurrent.Lock, 目前还不支持 -
显示指定状态的线程
$ thread --state TIMED_WAITING
线程状态一共有 [RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, NEW, TERMINATED] 6 种
-
查看指定线程的运行堆栈
$ thread 46
介绍了几种常见的用法,在实际排查中需要针对我们的现场做针对性的分析,也同时考察了我们对线程状态的了解程度。我这里列举了几种常见的线程状态:
初始(NEW)
新创建了一个线程对象,但还没有调用 start() 方法。
运行(RUNNABLE)
Java 线程将就绪(ready)和运行中(running)两种状态笼统的称为“运行”
阻塞(BLOCKED)
线程阻塞于锁
等待(WAITING)
进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)
- Object#wait() 且不加超时参数
- Thread#join() 且不加超时参数
- LockSupport#park()
超时等待(TIMED_WAITING)
该状态不同于 WAITING,它可以在指定的时间后自行返回
- Thread#sleep()
- Object#wait() 且加了超时参数
- Thread#join() 且加了超时参数
- LockSupport#parkNanos()
- LockSupport#parkUntil()
终止(TERMINATED)
标识线程执行完毕
状态流转图
问题分析
分析线程池满异常并没有通法,需要灵活变通,我们对下面这些 case 一个个分析:
thread --state
thread -n
thread
thread ${thread_id}
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Python:线程之定位与销毁
- 认识绝对定位,相对定位
- 移动端页面头部固定定位的绝对定位实现
- webgl(three.js)实现室内定位,楼宇bim、实时定位三维可视化解决方案——第五课
- IP 地址怎么定位?
- # CSS 绝对定位释义
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Perl语言入门 第六版(中文版)
Randal L.Schwartz、brian d foy、Tom Phoenix / 盛春 / 东南大学出版社 / 2012-3 / 62.00元
《Perl语言入门(第6版)(中文版)》根据作者施瓦茨、福瓦、菲尼克斯从1991年开始的教学经验积累汇聚而成,多年来十分畅销。此次第六版涵盖了最新的Perl5.14版本的变化。《Perl语言入门(第6版)(中文版)》每章都包含若干习题,帮助你巩固消化刚学到的知识。也许其他书籍只是想着灌输Perl编程的条条框框,但《Perl语言入门(第6版)(中文版)》不同,我们希望把你培养成一名真正的Perl程序......一起来看看 《Perl语言入门 第六版(中文版)》 这本书的介绍吧!