一文带你了解Monitor线程监控
如果您正在寻找一种有效方式,监控和管理Java虚拟机(JVM)中的线程,那这篇文章可太适合你啦!
本文详细介绍了Monitor线程监控工具,这是一种强大的实时分析工具,能够帮助您深入理解线程的运行情况和性能瓶颈。无论您是系统管理员还是性能分析师,通过本文的阅读,您将掌握使用Monitor工具优化系统性能的方法,性能问题的快速定位及解决将不再是难题,一起来看看吧~
功能简介
Monitor线程监控能够对 JVM 的当前线程堆栈快照进行分析,类似 Java 命令 Jstack,是实时分析线程运行情况和请求卡顿的好工具。
线程监控适用于当系统没有慢请求但存在卡顿的场景,我们可以通过持续观察系统所有线程状态,执行过程中查看线程信息、线程统计、当前方法、线程堆栈动态变化情况,综合分析定位出性能卡顿的根因。
适用版本
适用于所有版本,但不同版本界面稍有差异,本文将以金蝶云·苍穹V6.0.1版本的界面为例进行讲解。
功能入口
线程监控有两种查看方式,一种是在【注册中心】,选择某个服务实例,对单实例进行分析;另一种是在一级菜单【集群监控】→【线程监控】,对集群所有实例进行分析。
功能详情
1. 功能特性介绍
相对于 Jstack,线程监控工具支持了一些高效的增强功能:
可以按是否活跃、Web/服务/MQ/调度线程等维度进行过滤。一般只分析活跃线程。
按照时间降序排序,即执行时间越久的线程排名越靠前。通过刷新可以看到耗时久的线程,方便分析性能卡慢。
实时统计指标,当前线程运行过程中产生的指标,比如访问 db 次数,访问 redis 次数,一目了然知道该线程运行时间的消耗点。
当前执行方法的上下文,如当前方法是执行 sql,可以显示执行的 sql 语句及耗时,申请分布式锁,当前申请的锁 key,以及耗时。如果当前方法是性能瓶颈,很容易就能看出来问题。
跨节点调用关系,在集群线程监控中,对一次请求调用,如果存在调用关系,以并排方式显示,例如Web 线程=>MServiceBOS 线程。
2. 注册中心-线程堆栈
下面我们介绍【注册中心】下的线程堆栈功能。勾选某服务节点,点击“线程堆栈”,页面如下图:
首先,有 6 个页签用于过滤,分别表示:
All thread:查看所有线程。
Living thread:只查看活跃线程,包括 web 和服务线程。判断活跃线程的依据是堆栈行超过 20 行,不是 JVM 中 Thread 的状态为 Running,注意这个是和 Jstack 的差别所在。
Web request thread:只查看 Web Http 线程,一般情况,只有 Web 节点有 Web 线程,除非没有独立部署 Web 节点,MService 节点同时充当了 Web 的功能。
Living web request thread:只查看活跃 Web 线程。
Service thread:只查看 RPC 服务线程,如果 RPC 是用 Dubbo,那么就是 Dubbo 服务线程,如果 RPC 用 OpenFeign,那就是 OpenFeign 服务线程。无特殊说明,本文后续默认用 Dubbo 来说明。
Living service thread:只查看活跃 RPC 服务线程。
在页签下面一行,是线程状态统计值,这个状态是 Java 中 Thread 的状态,具体解释可以参考 Java 文档,包括:
TIMED_WAITING:指包含时间的等待线程,如sleep(timeout),wait(timeout)。
RUNNABLE:指运行中的线程。
WAITING:指等待线程,如 Object.wait。
BLOCKED:指受阻塞并且正在等待监视器锁的某一线程的线程状态。
在状态文本上悬浮,可以显示进一步的线程分类统计,如下图:
下面介绍“线程堆栈”的结构,如下图:
(1)线程信息
线程名:http-request-pool-75/traceId:4757f020037e5011/time:1700300282268,其中“http-request-pool-75”这部分是每种线程池初始化时的一个初始名称前缀。
通过这个能大概判断出线程的类型和作用,此处可以识别出是 web 调用的线程。
“traceId”是请求链路的唯一标识,该请求线程开始执行时是动态拼接的,请求执行完恢复原始的线程名。也有些线程没有traceId,说明该线程可能处于闲置状态,或者是一些第三方组件创建的线程。平台生成的线程真正执行的时机一般都有 traceId。
“time”是指当前线程开始执行请求的时间,即启动时间,也是动态拼接的,请求执行完恢复原始的线程名,但是这个要看启动这个线程的线程池是否会执行完一次任务就重置这个时间,也许有的线程不会。有的线程也没有这个,解释跟traceId 一样。
启动时间:就是 time 转化后的值,将 long 类型的值转为 Date 类型后的值就是启动时间。
已运行:当前时间减去启动时间得到已运行时间。
URL: 当前线程执行请求的路径。
TenantId:租户 ID。
UserName:当前操作的用户名。
FormId:表单名称。
Action:表单上执行的方法。
(2)线程统计
非常重要,指线程从启动到当前时间,发生的一些行为指标的统计信息,一般会包括 redis 访问、数据库访问、Orm 访问、插件执行等指标类型,统计值包括执行次数、总时间、平均时间。通过线程统计功能,通常就能够直观分析出本次线程执行中的主要耗时。
(3)当前方法
显示当前执行方法的信息,不是所有方法和时刻都能抓到,只有一些核心方法,刚好比较耗时的时候可以抓到,如下图所示,当前正在执行 SQL 查询,而 SQL也比较耗时,所以可以被看到。能够查看的方法主要有 SQL 执行、访问 Redis、微服务RPC 调用、申请分布式锁等。
开始时间是指当前方法已执行多少毫秒,执行方法是具体的方法描述,下面一行是该方法的参数,如 sql 语句,或者申请的分布式锁的 key,请看下图:
(4)线程堆栈
这个是通过 Java 程序的 Thread.getStackTrace() 获取到的当前时间点上,程序正在执行的调用路径。与 JVM 命令的 Jstack 得到的堆栈信息有一些区别。
3. 集群监控-线程监控
“线程监控”类似于注册中心的“线程堆栈”,区别是“线程监控”是从整个集群来分析线程的,具有更多的功能。一般来说,可以用本功能替代注册中心的“线程监控”。
线程展示结构和线程堆栈中介绍的类似,不再重复介绍,本节只介绍差异部分,如下图:
(1)过滤功能
使用者可以按照线程类型(活跃线程、Web 线程、服务线程,MQ线程等)、微服务、服务实例等维度进行过滤,也可以进行关键字或者排除关键字的过滤。
(2)集群排序
将整个集群所有节点的线程,按照耗时时长进行排序展示,故可以从整个集群范围查看耗时的线程,无需一个个节点打开分析。
(3)调用关系并列展示
对一次请求调用,如果存在调用关系,以并排方式显示,例如 Web 线程=>MServiceBOS 线程。如下图:
左边的堆栈信息是发起 http 请求时在 web 节点产生的堆栈信息,当前方法是在做跨节点服务 RPC 调用,右边是被调用节点当前的堆栈信息,通过==>来标识了调用方向。
该功能可以直观看到一个请求在节点上的调用拓扑,无需跨节点按 traceId 关系去查找。
对于稍瞬即逝的线程分析过程,分析的实时性直观性是非常重要的。
(4)死锁检测
当节点内 Java 发生死锁(即 synchronize,Object.wait 等产生的死锁,不包括 Java 轻量级锁),线程堆栈能在界面上方直接显示出来,如下图,前两个线程互相等待,导致死锁。关键字:“线程 1 Blocked on 锁对象 owned by Thread 线程 2”。
4. 新版线程监控
为了匹配用户的使用习惯,新版本线程监控的入口在原界面的右下角【体验新版】进入,新版线程监控在原来的基础上界面功能重新做了调整,线程展示结构和线程监控中的还是一样,本节只介绍差异部分。
线程类型可按单选/多选过滤。
线程状态可按是否活跃过滤。
包含关键字/排除关键字可过滤线程名和线程信息。
线程运行时间可过滤线程已运行时间,有些线程没有时也会过滤。
自动刷新设置后可实时刷新线程堆栈。
“下载”按钮可以将当前线程堆栈页面离线下载为HTML文件,方便离线排查。
左侧侧边栏展示的是“微服务列表”,下拉框可以单选/多选过滤同一APPName的服务,不选则显示全部。微服务列表同样可以单选/多选/全选过滤。效果如下图:
注意事项
线程监控是 Monitor 中最重要的功能,运维人员或性能分析师应该养成习惯,登录 Monitor查看线程监控,检查是否有异常线程。如果这个界面打开非常慢,而 Monitor 其他界面都很快,那么就是符合条件的某些节点服务有异常,需要关注。
应用案例分析
接下来介绍两个应用案例,帮助大家更好地理解Monitor线程监控工具的使用方法及应用价值。
案例一:功能卡顿排查
当首页加载或者进入某个列表时,页面很久都加载不出来,可能调用链条非常长,我们不知道具体是在哪个方法上面卡住了,一段代码一段代码的分析不仅困难而且费时。
此时我们可以去查看堆栈信息,过滤“所有活跃线程”,线程堆栈里面排在前面的线程就是运行时间较长的,有上下文信息及线程统计信息的一般就是卡顿的所在,如果多次点击刷新,线程堆栈都停留在同样的方法调用上,那说明可能该方法是比较耗时的,可以着重去排查下代码是否会存在比较耗时的场景。
#注意
有些线程虽然排在前面,但可能是一些后台常驻线程,通过线程名用途忽略掉。比如临时表相关的线程,线程名中包括“TempTable”。
如下图,耗时 6630 秒,连续多次刷新,堆栈都停留在同一个地方,则可基本判定是卡在了该方法,这是一个 BOTP代码,在批量转换单据,基于此我们可以找对应模块的开发同事进一步确定根因。
有时候,也可以根据线程统计中的信息发现是哪里执行比较耗时,如:
基于上图分析,如 DB 执行次数 1115 次,那么一次请求执行这么多次的 db 操作是否有必要,能否进行逻辑的优化?
导致卡顿,也有可能是线程被阻塞了。
从上面的堆栈可看出,是在获取锁时发生了阻塞等待,导致线程一直停滞在了这里,等待的原因是当前方法中的 DLock.acquire,即申请分布式锁,因为分布式锁同时只能有一个线程持有,持有者线程还在执行业务逻辑,未释放锁。
某些情况下,会出现很多线程在等待同一个锁的情况,这个时候就可以:① 优化持有锁的执行逻辑,让它变快;②申请锁的线程数控制,防止大量线程等待,耗尽 JVM 线程。
案例二:死锁分析
线程堆栈信息能及时发现死锁信息,会以红色字体的警告形式出现在首行位置。
从图中我们可以看出是DubboServerHandler-172.16.8.167:20880-thread-1与DubboServerHandler-172.16.8.167:20880-thread-5发生了循环获取锁的情况,但是从此图还不能完全还原相互获取锁的一个链路过程,下面再分别看下这两个线程的栈信息。
线程1先从PromptWordCacheNew.getPromptWord处获取锁,再从TypesContainer.getOrRegisterSingletonInstance获取锁。
线程5先从TypesContainer.getOrRegisterSingletonInstance处获取锁,再从PromptWordCacheNew.getPromptWord获取锁。
由于两个线程分别调用这两个方法的顺序相反,且这两个方法都加了锁,就造成了死锁。
划重点
本文全面介绍了Monitor线程监控工具,为Java虚拟机(JVM)的线程分析提供了一种实时、多维度的监控解决方案,主要包括以下几个方面:
1. 工具概述:概述了Monitor工具的基本功能和适用版本,使读者对工具有一个基本的认识。
2. 高级功能解析:本文深入探讨了Monitor线程监控工具的四大高级特性:过滤、排序、统计和堆栈分析。通过详细解释这些功能的工作原理和使用方法,展示了如何有效地利用它们来识别和定位性能瓶颈以及死锁等线程相关问题。
3. 应用案例分析:通过两个案例,演示了如何利用Monitor工具进行问题的快速排查和性能优化。进一步验证了工具的实际应用价值。
4. 运维常规检查的重要性:本文强调了将线程监控作为运维常规检查的重要性,并给出了使用Monitor进行日常监控的建议。鼓励大家将线程监控纳入日常运维工作中。
Monitor线程监控工具为JVM线程分析提供了强大的支持,通过其高级功能和丰富的应用案例,我们可以更加高效地识别和解决线程相关问题,提升系统的稳定性和性能。
#往期推进#
更多精彩内容,“码”上了解!↓
一文带你了解Monitor线程监控
本文2024-09-23 00:27:51发表“云苍穹知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-cangqiong-139626.html