likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 8

一、一个难查的生产问题

一天,小王发现生产环境上偶发性地出现某接口耗时过高,但在测试环境又无法复现,小王一筹莫展😔。小王“幻想”到:如果有个工具能记录生产上各个函数的耗时该多好,这样一看不就知道时间花在哪了? 这不是幻想,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 终端输入以下命令:

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

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

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

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

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

    [arthas@5555]$ trace --skipJDKMethod false demo.MathGame run
    

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

    太方便了!Arthas,生产问题大杀器run方法的trace流

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

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

    拦截 primeFactors 方法抛出的异常:

    [arthas@5555]$ watch demo.MathGame primeFactors -e "throwExp"
    

    太方便了!Arthas,生产问题大杀器拦截异常

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

3.2.CPU 占用过高

CPU 是程序运行的核心计算资源,一旦出现 CPU 占用过高,必定对大部分用户的访问耗时产生影响。针对这类问题,要定位出有问题的线程,并获取该线程当前执行的代码位置。 使用 top + jstack 命令可以定位这类问题(见参考资料三),Arthas 也提供了更便捷的一体化工具:

  1. 定位目标线程

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

    太方便了!Arthas,生产问题大杀器示例程序的CPU占用不算高

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

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

    # “1” 是上一步定位到的 main 的线程ID
    [arthas@5555]$ thread 1
    

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

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

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

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

  1. 反编译代码
    # demo.MathGame 是目标类的全限定名
    [arthas@5555]$ jad demo.MathGame
    

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

  1. 查看目标类所属的依赖包

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

    太方便了!Arthas,生产问题大杀器目标类所属的包

    如果这里是依赖包,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)
转载自:https://juejin.cn/post/7308230350374256666
评论
请登录