背景
近期,闲鱼核心应用出现了一个比较难解决的问题。在该应用集群中,会随机偶现一两个实例,其JVM运行在一个挂起的状态,深入分析stack文件发现,此时jvm中有大量的线程在等待一把没有任何线程持有的锁。问题实例所在的机器负载相对正常机器要轻很多,而其线程数则大幅增多。由于该问题在逻辑上的冲突,加上周边问题的复杂性,使得研究、分析、解决该问题变得相对来说困难与曲折。本文将系统性地介绍如何解决这个问题,并找出问题背后的原因。
问题分析
在实际解决这个问题的时候,我们发现不仅问题本身显得不合常理,其周边环境也相对来说不友善,给问题的分析与解决带来了较大的困难。
集群中随机出现。问题随机出现在该应用集群中的一个或几个实例中,无法提前预知其出现规律。
单机出现时间不可预知,现场捕捉困难,捕捉风险大,一般发现已经为事后,无现场第一手数据。从单个机器,或者单个实例看,则是出现概率非常低,出现时间完全随机。这使得蹲点单台机器以捕捉这个问题的思路几乎行不通,策略扩大至整个集群又可能出现稳定性及性能问题。
问题出现频率低。出现频率大概在一到两天一次。
问题表现复杂。该问题的表现很复杂,不仅从第一眼看去不合常理,JVM内部出现了大量线程在等待一把没有任何线程持有的锁。另外,问题机器的负载非常低,基本上在5%以内,相当于空载,而JVM中线程数却非常多,最多发现过接近4k个线程。
问题周边环境复杂。该问题出现前后,应用先后引入了rxjava、协程,应用为早期应用,服务结构复杂,而log4j问题又和网上大量的文章情形不符。
验证困难。理论分析完成后,无法在线上复现及验证,安全性、稳定性、数据等都不允许直接在线上验证。
解决方案
解决这个问题的主要按照以下六步,一步步排除法,最终定位并解决问题。按照先易后难,先直接后理论,先数据后源码的顺序,总结出来以下六步,大体上投入时间逐步增加,难度也逐渐增加。
step1. 代码bug查找
代码问题指的是业务代码本身逻辑问题把JVM带入了某种故障状态。问题的分析及排除很简单,通过观察应用日志即可。
step2. 现场捕获
定位了问题,问题也就解决了一半。

一般来说,定位问题主要有两个分类,即时定位,事后定位。
前一种是指我们实时直接监控JVM信息,在关键信息异常时,即发生动作。配合周期性的信息采集,基本可以对问题发生时刻前后数据精准采集和对比,做法一般是采用JVM代理方式或JMS方式。JVM代理分为C语言和Java语言代理,C语言代理运行在JVM层,可以做到即时Java代码发生

本文详细记录了一次线上JVM问题的排查过程,问题表现为大量线程等待一把无人持有的锁。分析涉及代码bug查找、现场捕获、资源耗尽检查、框架层源码阅读等步骤,最终发现线程池结构失衡,特别是HSF框架中Provider与Consumer线程池比例严重失调,导致回环调用使应用服务能力下降,形成恶性循环,使得JVM运行缓慢甚至挂起。实验验证通过压测平台证实了问题的诱因,为解决此类问题提供了思路。
最低0.47元/天 解锁文章
1440

被折叠的 条评论
为什么被折叠?



