太方便了!Arthas,生产问题大杀器

一、一个难查的生产问题

一天,小王发现生产环境上偶发性地出现某接口耗时过高,但在测试环境又无法复现,小王一筹莫展😔。小王"幻想"到:如果有个工具能记录生产上各个函数的耗时该多好,这样一看不就知道时间花在哪了?

这不是幻想,Arthas 已经帮我们解决了这个问题。在介绍它之前,我们先了解下相关背景。

二、动态追踪

现在互联网和大家生活的各个方面都息息相关。相应地,互联网应用的用户规模也变得越来越大。江湖大了,什么风浪都有。开发者们不断被各种诡异问题打扰,接口耗时过大、CPU 占用过高、内存溢出、只有生产环境会报错 ......

这些问题出现的概率可能是千分之一、乃至万分之一。如果我们能不修改代码、不修改配置、不重启服务 ,就能看到程序内部在执行什么 ,这该多好,再大的问题心里也有底了。

动态追踪技术出现了,它诞生于21 世纪初。Sun Microsystems 公司的工程师在解决一个复杂问题时被繁琐的排查过程所困扰,痛定思痛,他们创造了 DTrace 动态跟踪框架。DTrace 奠定了动态追踪的基础,Bryan Cantrill, Mike Shapiro, and Adam Leventhal 三位作者也多次获得行业荣誉。

动态追踪技术出现的时间早,但 Java 语言相关的调试工具链一直不太完善。直到进入移动互联网时代,Java 的发展才进入了快车道。2018 年,Alibaba 开源 Arthas,Java 的动态追踪才真正好用起来

动态追踪可以看作是构建了一个运行时"只读数据库" ,这个数据库内部保存了实时变化的 进程运行信息,我们通过调用这个"数据库"开放的接口,就能看到进程内部发生了什么。

经验丰富的读者可能会有疑问,现在微服务都用上了 Skywalking 这样的分布式链路追踪技术,通过它也能分阶段地看到各个部分的执行情况,为什么还需要 Arthas?

Arthas 有两大特点:

  1. 低侵入;不需要程序中进行额外配置,更不需要手动埋点。
  2. 功能强大;Arthas 提供了四十多种命令:从查看线程调用链,到查看输入、输出,到反编译代码等,应有尽有。

对于排查接口耗时长这样的情况,Skywalking 可以和 Arthas 配合起来,先用 Skywalking 定位出异常微服务,再用 Arthas 分析单个进程的情况,找到根因。

三、Arthas常用场景

相信你对动态追踪有了基本的了解,Arthas 可以理解为动态追踪在 Java 领域落地的具体工具。下面以场景助学,大家可以参考这些方案,因事制宜来解决自己的问题。

Arthas 的安装和基础使用见官方文档:Introduction | arthas

3.1.接口慢/吞吐量低

在文章开头,小王就遇到了这个问题。现在小王依靠老道的排查经验确定了 MathGame 服务肯定有问题,但具体的点却找不到。小王仔细学习了这篇文章,决定用 Arthas 分以下三步来排查:

  1. profile 明确整体的耗时情况

    profile 命令支持为应用生成火焰图,在 Arthas 终端输入以下命令:

    shell 复制代码
    # 开始对应用中当前执行的活动采样 30 秒,采样结束后默认会生成 HTML 文件
    [arthas@5555]$ profiler start -d 30

    打开 HTML 文件能看到这样的结构:

    火焰图

    MathGame 类 下的 run 方法占用了大部分的执行时间,接下来我们看看 run 方法内部的耗时情况。

  2. trace 详细查看单个调用的内部耗时

    shell 复制代码
    [arthas@5555]$ trace --skipJDKMethod false demo.MathGame run

    PrintStream 类print 方法 占据了 87% 的时间,这是 JDK 自带的类,这说明我们程序本身并无耗时问题,但 MathGame 类的 primeFactors 方法抛出了异常,我们可以看看具体的异常,再思考怎么优化。

    run方法的trace流

    另外,trace 可以选择性地进行调用拦截,比如设置只拦截大于 20ms 的调用:

    shell 复制代码
    [arthas@5555]$ trace demo.MathGame run '#cost > 20'
  3. watch 查看真实的调用数据

    拦截 primeFactors 方法抛出的异常:

    shell 复制代码
    [arthas@5555]$ watch demo.MathGame primeFactors -e "throwExp"

    拦截异常

小王从大到小、逐步分析,找出了问题的原因是 primeFactors 抛出了异常,修正参数后,程序恢复了正常。

3.2.CPU 占用过高

CPU 是程序运行的核心计算资源,一旦出现 CPU 占用过高,必定对大部分用户的访问耗时产生影响。针对这类问题,要定位出有问题的线程,并获取该线程当前执行的代码位置

使用 top + jstack 命令可以定位这类问题(见参考资料三),Arthas 也提供了更便捷的一体化工具:

  1. 定位目标线程

    shell 复制代码
    # 调用线程看板,并刷新数据三次
    [arthas@5555]$ dashboard -n 3

    示例程序的CPU占用不算高

    DashBoard 刷新三次后,在最新状态中发现示例程序里自己的线程 "main" 占用不算高。说明程序运行正常。如果是要排错,这里就要找出 CPU 占用最高的用户线程的 ID

  2. 查看目标线程执行的代码位置

    shell 复制代码
    # "1" 是上一步定位到的 main 的线程ID
    [arthas@5555]$ thread 1


    线程正在"睡觉",没什么大问题。

3.3 生产环境的效果和测试不一样

有些时候你发现:测试环境正常,但生产就报错了。这类问题主要靠做好上线流程的管控,但也有可能是打包的依赖库出现冲突,造成程序行为不一致。接下来,我们看看怎么用 Arthas 反编译代码,以及怎么对比依赖库的版本。

  1. 反编译代码

    shell 复制代码
    # demo.MathGame 是目标类的全限定名
    [arthas@5555]$ jad demo.MathGame
  1. 查看目标类所属的依赖包

    shell 复制代码
    # demo.MathGame 是目标类的全限定名
    [arthas@5555]$ sc -d demo.MathGame

    目标类所属的包
    如果这里是依赖包,code-source 还可以显示所属包的版本。这样就可以对比本地的代码,从而在打包时设置正确的依赖版本。

3.4 内存溢出

生产问题中内存溢出也有不小的比例。内存溢出的关键是找出高内存占用的对象。命令行操作会比较麻烦,建议转储 Heap Dump 等文件后,通过 Eclipse Memory Analyzer(MAT) 等工具进行分析。

四、运行 Arthas 报错

在有些运行环境下,Arthas 会出现报错。对于以下两种情况,读者可参照文档解决:

五、参考资料

  1. 动态追踪技术漫谈 - OpenResty 官方博客
  2. DTrace - Wikipedia --- DTrace - 维基百科,自由的百科全书
  3. 用JStack和Top分析Java进程CPU占用率_java top threads-CSDN博客
  4. Introduction | arthas --- 简介 |阿尔萨斯 (aliyun.com)
  5. 如何读懂火焰图? - 阮一峰的网络日志 (ruanyifeng.com)
相关推荐
极客悟道3 分钟前
巧解 Docker 镜像拉取难题:无需梯子和服务器,拉取数量无限制
后端·github
代码的余温11 分钟前
5种高效解决Maven依赖冲突的方法
java·maven
慕y27414 分钟前
Java学习第十六部分——JUnit框架
java·开发语言·学习
paishishaba20 分钟前
Maven
java·maven
aiopencode22 分钟前
iOS 出海 App 安全加固指南:无源码环境下的 IPA 加固与防破解方法
后端
liangdabiao26 分钟前
AI一人公司?先搞定聚合支付!一天搞定全能的聚合支付系统
后端
AillemaC32 分钟前
三分钟看懂回调函数
后端
yeyong33 分钟前
越学越糟心,今天遇到又一种新的服务控制方式 snap,用它来跑snmpd
后端
喷火龙8号36 分钟前
深入理解MSC架构:现代前后端分离项目的最佳实践
后端·架构
张人玉1 小时前
C# 常量与变量
java·算法·c#