ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Arthas生产实战:Java线上故障排查的核心命令与技巧

Arthas生产实战:Java线上故障排查的核心命令与技巧 Arthas这个工具我用了三年从第一次在线上事故中用它救场之后就再也没放下过。如果你还在靠jstack一把梭、靠重启治百病这篇教程务必看完。我不会讲那些官方文档里复制粘贴的废话只讲实际排查中真正高频的命令、真正能救命的使用姿势以及那些官方教程懒得告诉你、只有踩过坑才懂的细节。1. 为什么生产环境诊断工具最终选了Arthas先聊个扎心的场景周五晚高峰线上服务突然CPU飙到99%接口大面积超时监控报警响成一片。这时候你手上有什么jstack抓线程快照?抓完发现线程栈里全是一堆线程池等待根本看不出谁在疯狂消耗CPU。想用jmap dump堆内存一个生产实例堆可能十几个Gdump完分析工具都要加载半小时。更麻烦的是jstack只能拿到当前一瞬间的线程状态而CPU毛刺往往稍纵即逝等你反应过来抓第二把现场早就变了。这就是Arthas能站起来的原因。它不需要重启应用直接attach到目标JVM进程上像一把手术刀一样插进生产环境在不影响业务的前提下做各种实时诊断。最狠的是它能把命令执行在每个方法调用层面让你看到线上代码到底在执行什么逻辑、入参是什么、返回值是什么、异常是什么。举个例子生产环境出了个诡异Bug只有特定用户账号会触发本地复现不了代码逻辑翻来覆去看不出毛病。用Arthas一条watch命令把那个方法盯住看它的入参和返回值问题当场现形。Arthas不是用来替代jstack和jmap的也不是用来替代JVisualVM的。它解决的是这些传统工具解决不了的问题线上环境不允许随便重启生产日志不够细本地无法复现。在这些场景里Arthas几乎是唯一能在几分钟内给你答案的工具。官方给它的定位是Java诊断工具我自己的理解是它就是JVM里的瑞士军刀。而且它是阿里开源的GitHub上star数量常年霸榜社区活跃度极高遇到问题基本都能搜到答案。2. 先跑起来安装和启动Arthas的完整姿势Arthas的安装可以说是所有Java工具里最友好的了不需要复杂的配置不需要装什么额外的依赖前提是你的服务器上装了JDK。2.1 下载和启动进入生产服务器或者本地环境执行curl -O https://arthas.aliyun.com/arthas-boot.jar下载完是一个boot启动器运行它java -jar arthas-boot.jar它会列出当前机器上所有Java进程像这样一个列表[1]: 8821 com.example.OrderServiceApplication [2]: 15234 org.apache.catalina.startup.Bootstrap输入进程前面的序号回车Arthas就attach上去了。看到Arthas的logo和命令行提示符说明你已经成功进入目标JVM。提示如果服务器上同时跑了几十个Java进程列表会很长推荐用java -jar arthas-boot.jar 8821这种方式直接指定PID省去查找的麻烦。也可以用ps -ef | grep java先找到目标进程的PID再传进去。2.2 列出所有命令的方式进入Arthas之后如果不太确定哪些命令可用直接敲help[arthas8821]$ help会列出全部命令每个命令后面还有简短的说明。想查某个命令具体怎么用用help 命令名[arthas8821]$ help watchArthas的命令大致分几类观测类watch、trace、stack、monitor、字节码增强类jad、mc、redefine、线程类thread、dashboard、类加载类classloader、sc、执行类ognl、exec、vmtool等。这几类的高频用法下面逐个拆解。2.3 退出和全局配置想退出当前attach输入quit或者exit退出Arthas控制台目标JVM会恢复正常不会留下任何残留。这点设计得很好——Arthas的所有增强都是临时的退出后自动失效不会污染生产环境。还有几个好用的全局设置# 结果输出中显示执行耗时默认是显示的但可以通过配置改格式 [arthas8821]$ options | grep -i timeoutoptions命令可以查看和修改Arthas运行时的所有配置项比如options save-result true可以把结果保存到文件里适合排查完把证据留存。options timeout可以设置命令执行超时时间避免某些慢方法让命令卡死。3. Arthas核心命令速攻从dashboard到ognl逐个击破3.1 dashboard第一眼看清全局进入Arthas后我通常先敲dashboard快速浏览整个JVM的现状[arthas8821]$ dashboard它会弹出一个面板展示线程概况、内存分布、GC情况、运行时信息。线程部分会直接列出CPU占用最高的线程及线程栈内存部分能一眼看出堆内存用了多少、老年代占比多少。这个命令的价值在于快速确定方向。CPU飙高先看哪个线程在抢CPU。内存吃紧先看各区域占比。GC频繁看GC次数和耗时。初步锁定问题方向之后再用下面这些命令精准定位。启动参数里推荐加一行[arthas8821]$ dashboard -n 5-n参数表示只刷新5次然后自动退出。不加这个参数它每5秒刷新一次刷个没完需要手动CtrlC中断。3.2 thread线程问题一查一个准thread命令是排查线程问题的主力用法非常灵活# 查看所有线程 [arthas8821]$ thread # 查看线程ID为42的线程栈 [arthas8821]$ thread 42 # 找出当前BLOCKED状态的线程死锁排查神器 [arthas8821]$ thread -b # 按CPU使用率排序展示前10个线程 [arthas8821]$ thread --top-n 10 # 找出指定状态的线程 [arthas8821]$ thread --stateWAITINGthread -b是我用得最多的它能直接定位到死锁。有一次排查一个订单状态卡住的问题业务日志没有异常DB锁也查了没问题最后thread -b一出来两个线程各持一把锁互等问题当场破案。CPU飙高时怎么用thread找凶手我的套路是先dashboard看哪个线程CPU高记下线程IDthread 线程ID查看该线程栈确认它在执行什么代码如果栈上能看到业务方法再用watch或trace钻进去看方法内部3.3 jad反编译线上代码再也不怕缓存和旧包jad命令反编译线上JVM正在运行的类这个功能简直是救命神器# 反编译com.example.OrderService类 [arthas8821]$ jad com.example.OrderService # 反编译指定方法 [arthas8821]$ jad com.example.OrderService getOrderInfo它能解决什么问题确认线上到底跑的是哪个版本的代码。经常有这种场景开发信誓旦旦说代码改过了但线上行为还是老样子。用jad一看反编译出来的代码里压根没有改过的那行逻辑——要么没发版要么打包打错分支要么缓存没清干净。铁证如山扯皮的功夫全省了。还有一个高频用法查看某个框架内部类到底做了什么。比如排查MyBatis生成的代理对象执行SQL时走了什么逻辑直接jad打开看内部实现比自己翻源码方便太多。反编译出来的代码会带行号可以对照watch里的行号信息精确定位问题代码位置。3.4 watch方法级别的全息监控watch是我在Arthas里使用频率最高的命令它能观察指定方法的入参、返回值、异常几乎覆盖了所有这个方法到底干了什么的需求。基本用法[arthas8821]$ watch com.example.OrderService createOrder {params, returnObj, throwExp} -x 2命令拆解com.example.OrderService createOrder类名和方法名{params, returnObj, throwExp}要观察的表达式分别代表入参、返回对象、异常对象-x 2结果展开的深度。方法返回体比较深时-x 1可能只显示对象的引用地址不显示内容-x 2或-x 3才能看到完整结构最常用的几种观察场景# 只看入参不看返回入参都打不出来时 [arthas8821]$ watch com.example.PaymentService callback {params[0]} -x 2 # 方法抛异常时打出来catch住但没记日志的异常 [arthas8821]$ watch com.example.PaymentService callback {throwExp} -x 2 # 根据条件过滤只有某类账号才触发观察 [arthas8821]$ watch com.example.OrderService createOrder {params, returnObj} params[0].getUserId() 123456有一次排查支付回调丢单问题业务方反馈偶尔有单子回调后状态没更新。用watch观察回调方法的入参发现有一批回调请求体里的订单号多了不可见字符导致查库匹配不上。这种问题如果不用watch靠日志分析不知道要排查多久。注意watch默认会持续输出直到CtrlC退出。在流量大的接口上慎用每个请求都会命中观察并打印结果会打印到怀疑人生。建议加上条件过滤只观察可疑的请求。3.5 trace方法内部耗时全链路拆解接口慢是另一大类经典问题。用户报障说某个接口要十几秒才返回到底慢在哪是外部调用慢是数据库查询慢是循环里有个大计算这时候trace命令出场。[arthas8821]$ trace com.example.OrderService createOrder这个命令会进入方法内部把方法内部每一步耗时全部列出来。比如---tracing... com.example.OrderService.createOrder ---[10ms] com.example.OrderService.checkStock() ---[3012ms] com.example.OrderService.paymentService.pay() ---[1ms] com.example.OrderService.saveOrder()一目了然慢就慢在pay()这一步耗时3秒。顺着pay()再trace一层[arthas8821]$ trace com.example.PaymentServiceImpl pay继续往下追直到看到哪一步Remote调用耗时异常。这种逐层下钻的排查思路比盲猜日志高效太多。trace命令本身会输出每个子调用的耗时分布#cost参数可以设置耗时过滤只打印超过指定毫秒数的调用避免输出太碎。3.6 redefine线上热更新能不用就别用redefine可以热加载新的字节码覆写线上正在运行的方法达到修改线上代码不用重启的效果。听起来很美好但我对它的态度是知道有这个功能但千万别指望靠它修复生产事故。用法是本地改好代码编译出OrderService.class文件上传到服务器redefine /path/to/OrderService.class风险在于redefine之后如果新代码有问题线上就会带着一个问题代码继续跑而且和原本部署的代码不一致后续排查、发布都会产生混乱。更麻烦的是redefine不影响已经被加载的类也不影响接口签名变化的情况。我实际用redefine的场景只有一个线上某个日志开关没开导致关键信息打不出来临时把日志等级改高重新编译redefine上去看完日志再redefine回去。应急排查可以别当常规武器用。3.7 ognlJVM里的JS什么都干得掉ognl命令可以在线执行表达式直接调用JVM中的静态方法、获取全局变量、修改配置值。这相当于给JVM装了一个动态脚本引擎。# 调用静态方法 [arthas8821]$ ognl java.lang.SystemgetProperty(java.version) # 获取Spring容器中bean的某个属性值 [arthas8821]$ ognl #beanNameuserService, org.springframework.context.ApplicationContextgetBean(#beanName).getConfig()还有更硬核的用法直接修改线上配置项的值。比如发现某个开关是false导致走了错误分支用ognl直接改成true不用发版。但这种操作同样要慎之又慎改动生产环境运行状态这件事本身就有不可控风险用于临时验证可以用于长期修复不靠谱。4. 一次典型的生产问题Arthas完整排错思路走一遍把上面这些命令串起来模拟一次真实的生产故障排查过程。4.1 场景还原线上订单系统突现大量订单超时用户下单后回调迟迟不来订单状态一直停在待支付。排查前已有的信息监控显示支付回调接口失败率上升但日志里没看到明显异常接口平均耗时从200ms飙升到3秒重启了几次短暂恢复后很快又复发4.2 排查链路第一步dashboard确认JVM整体状态——发现Old区使用率持续增长GC频繁CPU整体不高。第二步thread --stateBLOCKED查看阻塞线程——发现大量业务线程阻塞在获取DB连接上。第三步trace com.example.PaymentService handleCallback——发现耗时主要集中在数据库查询方法上单次查询要2.5秒而平时只要30ms。第四步深入数据库层trace com.example.mapper.OrderMapper selectByOrderNo——发现每次查询都触发了大量数据库IO等待。到这里问题已经从代码层转移到了外部依赖层——数据库出了问题。后续排查发现是数据库的连接池被打满慢SQL拖垮了整个服务。Arthas帮助我们把故障边界从代码问题快速收敛到DB问题这就是诊断工具的核心价值——缩小范围。如果不用Arthas可能还要在代码逻辑里翻半天而它用几分钟就锁定了方向。5. 生产环境使用Arthas的注意事项和坑Arthas虽然好用但在生产环境使用有几个坑和建议提前知道能少踩很多雷。5.1 权限和合规问题Arthas能反编译、能热更新、能执行任意表达式这意味着它在生产环境的权限极大。公司如果对生产环境操作有审计要求一定先确认Arthas的引入符合规范。我在团队里使用前专门在技术评审会上报备了用途和命令范围避免后续扯皮。5.2 对业务性能的影响虽然Arthas对目标JVM的性能影响微乎其微但在极高并发场景下watch或trace这类字节码增强操作依然会有少量额外开销。在核心接口上批量执行watch理论上会带来几个毫秒级别的RT增加。实际经验是低流量环境随便用高流量接口谨慎用用条件过滤限制观察范围即可。5.3 防火墙和网络环境Arthas本机attach基本不会遇到网络问题但远程连接Arthas会用到Telecom端口默认可能是随机分配或指定端口。跨网络访问时需要确保端口被放行否则连不上。5.4 JDK版本兼容性Arthas对JDK 6到11的支持很好JDK 8是最舒服的版本。JDK 9以上因为模块化问题有些场景需要额外配置。我遇到的坑是在JDK 11上有些命令输出格式异常升级Arthas版本后解决。保险起见尽量使用最新的Arthas版本。6. 把Arthas纳入你的日常运维工具箱Arthas不是只能在事故期拿出来用的工具日常开发、测试、上线验证都能用上。我在团队里推广的做法是联调阶段本地起服务遇到前端传参奇怪导致后端报错用watch直接看后端拿到什么参数两分钟定位是前端问题还是后端问题省去互相甩锅的时间测试环境排错测试反馈某个功能偶现失败在测试环境用trace观察关键链路比苦思冥想强多了上线后观察新功能上线后10分钟用dashboard观察内存和GC变化用trace确认关键接口响应时间正常确认无问题后再离开Arthas也有一些衍生生态值得关注Arthas Tunnel可以在多个服务器之间统一管理和转发命令方便在跳板机环境下使用arthas-spring-boot-starter可以在Spring Boot应用中直接通过HTTP接口调用部分Arthas命令便于已有监控平台集成。最后提一个我踩过坑后学到的经验Arthas命令能写到shell脚本里自动化执行。比如批量排查所有节点的线程状态可以在每台服务器上预先写好脚本故障发生时一键并行执行快速拿到多节点现场。平时花点时间把这些排查动作脚本化、模板化真出事时你会发现自己稳得像那个不需要睡觉的运维老哥。
返回列表