如何使用Arthas定位线上 Dubbo 线程池满异常
点击上方蓝色“程序猿DD”,选择“设为星标”
回复“资源”获取独家整理的学习资料!
来源 | 公众号「Kirito的技术分享」
前言
本文是 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 {@Overridepublic 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 面板,介绍一下各列的含义:
ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应
NAME: 线程名
GROUP: 线程组名
PRIORITY: 线程优先级, 1~10 之间的数字,越大表示优先级越高
STATE: 线程的状态
CPU%: 线程消耗的 CPU 占比,采样 100ms,将所有线程在这 100ms 内的 CPU 使用量求和,再算出每个线程的 CPU 使用占比。
TIME: 线程运行总时间,数据格式为
分:秒
INTERRUPTED: 线程当前的中断位状态
DAEMON: 是否是 daemon 线程
在空闲状态下线程应该是处于 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 一个个分析:
阻塞类问题。例如数据库连接不上导致卡死,运行中的线程基本都应该处于 BLOCKED 或者 TIMED_WAITING 状态,我们可以借助
thread --state
定位到繁忙类问题。例如 CPU 密集型运算,运行中的线程基本都处于 RUNNABLE 状态,可以借助于
thread -n
来定位出最繁忙的线程GC 类问题。很多外部因素会导致该异常,例如 GC 就是其中一个因素,这里就不能仅仅借助于
thread
命令来排查了。定点爆破。还记得在前面我们通过 grep 筛选出了一批 Dubbo 线程,可以通过
thread ${thread_id}
定向的查看堆栈,如果统计到大量的堆栈都是一个服务时,基本可以断定是该服务出了问题,至于说是该服务请求量突然激增,还是该服务依赖的某个下游服务突然出了问题,还是该服务访问的数据库断了,那就得根据堆栈去判断了。
总结
本文以 Dubbo 线程池满异常作为引子,介绍了线程类问题该如何分析,以及如何通过 Arthas 快速诊断线程问题。有了 Arthas,基本不再需要 jstack 将 16 进制转来转去了,大大提升了诊断速度。
本文通过OpenWrite的Markdown转换工具发布
关注我,回复“加群”加入各种主题讨论群
代码生成器:IDEA 强大的 Live Templates
Spring Boot 2.1之后如何在启动日志中打印请求路径列表
NASA立扫把挑战”?牛顿的棺材板都按不住啦!
如何干掉恶心的 SQL 注入?
Spring Boot 2.x 中使用国产数据库连接池Druid
如何使用Arthas定位线上 Dubbo 线程池满异常相关推荐
- Arthas | 定位线上 Dubbo 线程池满异常
作者 | 徐靖峰 阿里云高级开发工程师 前言 Dubbo 线程池满异常应该是大多数 Dubbo 用户都遇到过的一个问题,本文以 Arthas 3.1.7 版本为例,介绍如何针对该异常进行诊断,主要使 ...
- Dubbo线程池满导致宕机的案例分析解决
1 背景概述 线上事故:在做活动促销的时候,交易中台的商品服务发生了个别节点的宕机而此时间段内QPS并没有超过告警配置的线程数阈值1500. 于是决定做一次压测,当对自己的商品服务做压测20000个请 ...
- 记一次线上压测Dubbo线程池队列满的问题
本文记录一次线上全链路压测出现的Dubbo线程池队列满的问题. 1 问题描述 线上做全链路压测,其中涉及三个系统,调用关系A->B->C,均是dubbo调用.压测的时候C出现CPU满导致服 ...
- 有了这款工具,定位线上问题事半功倍|云效工程师指北
大家好,我叫刘玄,负责云效流水线的开发.程序员在日常工作中经常会遇到一些线上问题需要排查,本文的主人公程序员小张也不例外.但排查的过程却时常令他困扰不已.让我们一起看看他遇到了哪些问题,又是怎么解决的 ...
- 记几次 [线上环境] Dubbo 线程池占满原因分析(第三次:GC STW)
[线上环境] Dubbo 线程池占满原因排查系列 记几次 [线上环境] Dubbo 线程池占满原因分析(第一次:HttpClient) 记几次 [线上环境] Dubbo 线程池占满原因分析(第二次:C ...
- 前端录屏+定位源码,帮你快速定位线上bug
前言 如何快速定位线上bug,是多数开发者都会遇到的难题 web-see 前端监控方案,提供了 前端录屏+定位源码 方式,让bug无处藏身 这是前端监控的第二篇,该篇讲解如何实现错误还原功能,第一篇 ...
- 抓包神器 Wireshark,帮你快速定位线上网络故障(3)
1 复习:TCP 三次握手&四次挥手 正式分享之前,先简单复习一下 TCP 的三次握手.四次挥手. TCP 通过三次握手建立连接(一图解千愁): TCP 协议通过四次挥手断开连接(一图知所 ...
- 用了很多年Dubbo,连Dubbo线程池监控都不知道,觉得自己很厉害?
前言 micrometer 中自带了很多其他框架的指标信息,可以很方便的通过 prometheus 进行采集和监控,常用的有 JVM 的信息,Http 请求的信息,Tomcat 线程的信息等. 对于一 ...
- dubbo官方文档_不可忽视的Dubbo线程池
问题描述 线上突然出现Dubbo超时调用,时间刚好为Consumer端设置的超时时间. 有好几个不同的接口都报超时了 第1次调用超时,第2次(或第3次)重试调用非常快(正常水平) Dubbo调用超时的 ...
最新文章
- c语言easy,C语言easy….doc
- mac 下 mamp 配置虚拟主机步骤
- android 文件名 标题,有什么方法可以让Android的默认浏览器识别Content-Disposition:attachment下载中的非ASCII文件名?...
- dw html5怎么美化,DW CS5/CS6代码格式化、美化插件 Dreamweaver代码格式化美化插件
- 笔记-信息化与系统集成技术-人工智能的特点
- 在ubuntu 14.04搭建React Native for Android开发环境
- 休眠事实:始终检查Criteria API SQL查询
- kotlin 或 运算_Kotlin程序对两个数字执行算术运算
- C++获取多维数组维数
- 【面向对象】面向对象程序设计测试题11-类的高级特性测试题
- 判断用户是否开启定位功能 / 判断用户是否为应用程序开启定位功能
- python合并两个数据框_python-3.x - 如何使用匹配索引合并两个数据框? - SO中文参考 - www.soinside.com...
- 基于JAVASCRIPT操作XML的无刷新分页
- Hexo设置背景图片轮播效果
- R语言抽样并验证总体分别为正态分布、均匀分布、指数分布时样本均值的抽样分布
- 计算机课情感态度与价值观,浅谈信息技术课中情感态度价值观的培养
- 【花雕体验】05 搭建行空板开发环境之SSH连接与Jupyter编程
- 海思Hi3516新增sensor imx214 笔记
- 从零开始部署Node.js服务至阿里云ECS服务器并通过express框架实现外网IP访问html项目
- 科学计算机解三角函数方程,如何让计算器计算方程如何让fx-82ES的卡西欧计算器解一次,二次,或更高次数方程?是支持三角函数的!...
热门文章
- linux shell sshpass 远程服务器 批量修改密码
- jenkins 反序列化漏洞 cve-2017-1000353
- linux centos7 重启服务器报错 Run 'systemctl daemon-reload' to reload units
- linux c 信号量简介
- linux内存管理基本概念
- 窗体的扩展样式GWL_EXSTYLE: 用于SetWindowLong
- 读《Linux内核设计与实现》我想到了这些书
- Android项目目录结构分析
- using bgp data to find spammers
- Ubuntu下安装JDK1.7