Arthas在项目开发中的应用 - 线上故障定位

介绍

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。在线排查问题,无需重启;动态跟踪Java代码;实时监控JVM状态。
Arthas 支持JDK 6+,支持Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab 自动补全功能,进一步方便进行问题的定位和诊断。

Arthas能为我们解决哪些问题

  1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
  2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
  3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
  4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
  5. 是否有一个全局视角来查看系统的运行状况?
  6. 有什么办法可以监控到JVM的实时运行状态?

与SkyWalking的区别

Skywalking是一个可观测性分析平台(Observability Analysis Platform,OAP)和一个应用性能管理(Application Performance Management,APM)系统。

Quick Start

  1. 启动本地Java进程 (实例工程,空SpringBoot项目)

  2. 下载arthas-boot curl -O https://arthas.aliyun.com/arthas-boot.jar,此步骤仅完成基础包下载,大小为139K上下

  3. 执行命令 java -jar arthas-boot.jar,选择需要attch的Java进程,进入arthas的命令界面

  4. 基本操作(官网入门示例)

dashboard

执行dashboard,将展示当前进程的信息。如图:
通过此命令可以查看JVM的堆内存情况,以及活跃线程、JVM基本信息等内容。

jad反编译代码

通过jad指令,反编译class

watch方法执行数据观测(查看函数入参和返回值)

通过模拟对入参 *2 的程序,反应watch的强大。

通过watch命令,监听方法的一切行为。watch的参数比较多,以下摘抄自官网:

class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
express 观察表达式
condition-express 条件表达式
[b] 在方法调用之前观察
[e] 在方法异常之后观察
[s] 在方法返回之后观察
[f] 在方法结束之后(正常返回和异常返回)观察
[E] 开启正则表达式匹配,默认为通配符匹配
[x:] 指定输出结果的属性遍历深度,默认为 1

monitor方法执行监控(什么时候执行了)

参数名称 参数说明
class-pattern 类名表达式匹配
method-pattern 方法名表达式匹配
condition-express 条件表达式
[E] 开启正则表达式匹配,默认为通配符匹配
[c:] 统计周期,默认值为120秒
[b] 在方法调用之前计算condition-express

monitor -c 5 top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController index

每5秒监听一次,统计耗时、失败率、总次数、成功次数、失败次数等信息

通过表达式过滤不需要的请求方法调用。

trace方法内部调用路径,输出方法路径上的节点耗时(我调用了谁)

trace top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController index

因为代价比较高(这属于skywarking的工作范围),trace默认只支持一层的耗时分析。但官网提供了多层trace的方法,具体见:https://arthas.aliyun.com/doc/trace.html


stack方法输出当前方法被调用的调用路径(谁调用了我)

stack top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController getResult -n 3

tt 记录方法请求的信息,方便重做请求和查看结果
方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
watch指令复杂,通过tt记录请求的信息后面可以针对性分析

2. 进阶操作

热加载代码

首先声明:在生产环境热更新代码是不很不好的行为。
但是肯定有它使用的场景。

涉及到几个命令:

  1. jad(反编译指定已加载类的源码)

  2. sc(查看JVM已加载的类信息)

  3. mc(内存编译器,内存编译.java文件为.class文件)、

  4. redefine(加载外部的.class文件,redefine到JVM里)

  5. jad反编译代码,vim调整代码逻辑
    jad --source-only top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController > /tmp/IndexController.java

  6. 通过 sc 命令,找到类加载器
    sc -d *IndexController

  7. 通过 mc 内存编译java -> class

mc -c 31221be2 /tmp/IndexController.java -d /tmp

  1. 通过 redefine 热加载

redefine /tmp/top/imyzt/learning/arthas/arthaswebdemo/web/controller/IndexController.class

全流程

profiler🔥图

通过 profiler start/stop 获得一份程序的火焰图

火焰图查看工具

JDK JMC:https://github.com/openjdk/jmc
JProfiler(付费): https://www.ej-technologies.com/download/jprofiler/files

如何读懂火焰图?

如何读懂火焰图? - 阮一峰的网络日志
使用arthas+jprofiler做复杂链路分析 · Issue #1416 · alibaba/arthas

  1. 进阶使用&命令列表

https://arthas.aliyun.com/doc/advanced-use.html
https://arthas.aliyun.com/doc/commands.html

常用使用场景分析和讨论

同传统方式进行对比,最大的目的是为了解决目前低效的生产环境问题排查方式。

  1. 通过watch观察方法入参,是否可以客户反馈使用出了问题,但是又不知道小程序发过来的请求具体是啥?
  2. 通过stack观察,某个方法什么时候被调用了,被谁调用了?
  3. 通过tt记录请求信息,便于重做请求,模拟用户操作?
  4. 通过后台异步任务,观察定时周期出现问题的代码,阿里描述:当线上出现偶发的问题,比如需要watch某个条件,而这个条件一天可能才会出现一次时,异步后台任务就派上用场了,详情请参考这里

以上描述的操作虽然能解决很多问题,但是是否面临着一个更大的问题?

适合生产环境的实践

  1. Web_Console

在attrch成功之后,直接访问本地3658端口,可以通过web界面操作。

默认情况下,arthas只listen 127.0.0.1,所以如果想从远程连接,则可以使用 --target-ip参数指定listen的IP。

  1. Tunnel Server

下载arthas-tunnel-server,本地java -jar启动,Web界面监听8080端口,WebSocket通信监听7777端口。

java -jar arthas-boot.jar --tunnel-server 'ws://127.0.0.1:7777/ws'

可以通过http://127.0.0.1:8080/actuator/arthas访问本地,获得已连接到tunnel-server的arthas-client。密码在启动控制台。

通过上面的操作,虽然免去了去生产环境机器直接操作arthas这种不现实的问题,但是在Web界面操作还有一个问题,就是谁给我们绑定执行 java -jar arthas-boot.jar --tunnel-server 'ws://127.0.0.1:7777/ws' 这个命令呢.......

与SpringBoot集成,更适合生产环境的实践

  1. 增加Maven依赖
<dependency>
    <groupId>com.taobao.arthas</groupId>
    <artifactId>arthas-spring-boot-starter</artifactId>
    <version>3.5.2</version>
</dependency>
  1. 调整配置文件
server.port=8892

spring.application.name=arthas-demo

# 建议不指定, 会根据 spring.application.name 生成
#arthas.agent-id=arthas-demo
arthas.tunnel-server=ws://localhost:7777/ws
# -1会随机分配端口
arthas.http-port=-1
arthas.telnet-port=-1
  1. 查看agentId

  2. tunnel-server连接后操作

对性能的影响

通过spring-boot-starter的方式,在应用启动时,就对进程自动完成了attach,对于性能方面的影响是不大的,下面有两个官方的回复,从原理是解释了这个问题。

开发团队回复:

目前arthas-spring-boot-starter方式是长期启动状态,对程序的性能有什么影响吗? · Issue #1843 · alibaba/arthas
是否进行过性能评估,attach之后对原进程性能有多大的影响呢 · Issue #44 · alibaba/arthas

如何记住各种命令

https://arthas.aliyun.com/doc/idea-plugin.html

遇到的一些坑

Pid=1无法Attach

linux保护机制,jstack无法attach住pid<=5的进程,arthas也无法使用,通用解决方案是使用tini挂载java进程。
https://github.com/alibaba/arthas/issues/362

Arthas端口问题

应用被Arthas-Boot attch之后,包括应用本身的端口,同时还会监听另外两个端口:
默认情况下,Arthas的Telnet端口是3658,HTTP端口是8563,这个常常让用户迷惑。在新版本里,在3658端口同时支持Telnet/HTTP协议。
在浏览器里访问 http://localhost:3658/ 也可以访问到Web Console了。
在后续的版本里,考虑默认只侦听 3658端口,减少用户的配置项。

当启动tunnel-server后,8080与7777端口也被监听。

与Skywalking的兼容性问题

java.lang.ClassFormatError: null、skywalking arthas 兼容使用
当出现这个错误日志java.lang.ClassFormatError: null,通常情况下都是被其他字节码工具修改过与arthas修改字节码不兼容。
比如: 使用 skywalking V8.1.0 以下版本 无法trace、watch 被skywalking agent 增强过的类, V8.1.0 以上版本可以兼容使用,更多参考skywalking配置 skywalking compatible with other javaagent bytecode processing。

JVM版本问题

启动Arthas的Java版本和启动应用的Java版本要保持一致

推荐文章

  1. Arthas源码学习-1_慢一拍的coder-CSDN博客
  2. Arthas Tutorials
  3. 工商银行打造在线诊断平台的探索与实践

Arthas使用到的技术

Arthas运行原理

tt命令探究

  1. 执行 tt -t top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController index
  2. 下载正在运行的字节码文件 dump top.imyzt.learning.arthas.arthaswebdemo.web.controller.IndexController
  3. 查看字节码 javap -c -s -v -l xxx.class

相关文章:Arthas原理系列(四):字节码插装让一切变得有可能