首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java线上问题排查神器Arthas快速上手与原理浅谈

Java线上问题排查神器Arthas快速上手与原理浅谈

作者头像
Java3y
发布于 2019-11-18 07:14:22
发布于 2019-11-18 07:14:22
74000
代码可运行
举报
文章被收录于专栏:Java3yJava3y
运行总次数:0
代码可运行

本文公众号来源:后端技术漫谈

作者:蛮三刀把刀

本文已收录至我的GitHub

前言

当你兴冲冲地开始运行自己的Java项目时,你是否遇到过如下问题:

  • 程序在稳定运行了,可是实现的功能点了没反应。
  • 为了修复Bug而上线的新版本,上线后发现Bug依然在,却想不通哪里有问题?
  • 想到可能出现问题的地方,却发现那里没打日志,没法在运行中看到问题,只能加了日志输出重新打包——部署——上线
  • 程序功能正常了,可是为啥响应时间这么慢,在哪里出现了问题?
  • 程序不但稳定运行,而且功能完美,但跑了几天或者几周过后,发现响应速度变慢了,是不是内存泄漏了?

以前,你碰到这些问题,解决的办法大多是,修改代码,重新上线。但是在大公司里,上线的流程是非常繁琐的,如果为了多加一行日志而重新发布版本,无疑是非常折腾人的。

现在,我们有了更为优雅的线上调试方法,来自阿里巴巴开源的Arthas

下图是Arthas文档中对于为什么要使用它的描述,我进行了精简:

好了,前言已经超过字数了,哈哈,在本篇文章里,你能够了解:

  • Arthas使用实例:帮助你快速让你上手,拯救你的低效率Debug
  • 使用Arthas解决具体问题:看一下Arthas帮我拯救了多少时间
  • 相似工具:看看线上Debug还有没有别的工具可以使用
  • 原理浅谈:莫在浮沙筑高阁!你需要大概了解下Arthas的原理

相信我,Arhas觉得是你提升效率的利器,适合各种阶段的开发者,尤其适合我这种刚入门的新人(天天上班写Bug的人)。你不应该有这种东西是高阶程序员才应该去使用的思想,放心大胆的去用吧

线上Debug神器Arthas

Arthas使用实例

命令的详细文档请参考:

https://alibaba.github.io/arthas/commands.html

快速启动

快速启动它,你只需要两行命令:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
wget https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar

随后,在界面出现的进程中,选择你的程序序号,比如1

这样你就进入了arthas的控制台

基本使用

Arthas有如下功能:

1. 首先是我认为的“上帝视角”指令:Dashboard

当前系统的实时数据面板,按 ctrl+c 退出。 当运行在Ali-tomcat时,会显示当前tomcat的实时信息,如HTTP请求的qps, rt, 错误数, 线程池信息等等。

通过这些,你可以对于整个程序进程有个直观的数据监控

2. 类加载问题相关指令

SC:查看JVM已加载的类信息

通过SC我们可以看到我们这个类的详细信息,包括是从哪个jar包读取的,他是不是接口/枚举类等,甚至包括他是从哪个类加载器加载的。

上图中代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[arthas@37]$ sc -d *MathGame
 class-info        demo.MathGame
 code-source       /home/scrapbook/tutorial/arthas-demo.jar
 name              demo.MathGame
 isInterface       false
 isAnnotation      false
 isEnum            false
 isAnonymousClass  false
 isArray           false
 isLocalClass      false
 isMemberClass     false
 isPrimitive       false
 isSynthetic       false
 simple-name       MathGame
 modifier          public
 annotation
 interfaces
 super-class       +-java.lang.Object
 class-loader      +-sun.misc.Launcher$AppClassLoader@70dea4e
                     +-sun.misc.Launcher$ExtClassLoader@69260973
 classLoaderHash   70dea4e

SC也可以查看已加载的类,帮助你看是否有没有纳入进来的类,尤其是在Spring中,可以判断的你的依赖有没有正确的进来。

上图中代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 查看JVM已加载的类信息
[arthas@37]$ sc javax.servlet.Filter
com.example.demo.arthas.AdminFilterConfig$AdminFilter
javax.servlet.Filter
org.apache.tomcat.websocket.server.WsFilter
org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
org.springframework.boot.web.filter.OrderedRequestContextFilter
org.springframework.web.filter.CharacterEncodingFilter
org.springframework.web.filter.GenericFilterBean
org.springframework.web.filter.HiddenHttpMethodFilter
org.springframework.web.filter.HttpPutFormContentFilter
org.springframework.web.filter.OncePerRequestFilter
org.springframework.web.filter.RequestContextFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
Affect(row-cnt:14) cost in 11 ms.

# 查看已加载类的方法信息
[arthas@37]$ sm java.math.RoundingMode
java.math.RoundingMode <init>(Ljava/lang/String;II)V
java.math.RoundingMode values()[Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(I)Ljava/math/RoundingMode;
java.math.RoundingMode valueOf(Ljava/lang/String;)Ljava/math/RoundingMode;
Affect(row-cnt:4) cost in 6 ms.

jad:反编译某个类,或者反编译某个类的某个方法

上图中代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 反编译只显示源码
jad --source-only com.Arthas
# 反编译某个类的某个方法
jad --source-only com.Arthas mysql


[arthas@37]$ jad demo.MathGame

ClassLoader:
+-sun.misc.Launcher$AppClassLoader@70dea4e
  +-sun.misc.Launcher$ExtClassLoader@69260973

Location:
/home/scrapbook/tutorial/arthas-demo.jar

/*
 * Decompiled with CFR.
 */
package demo;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;

public class MathGame {
    private static Random random = new Random();
    public int illegalArgumentCount = 0;

    public List<Integer> primeFactors(int number) {
        if (number < 2) {
            ++this.illegalArgumentCount;
            throw new IllegalArgumentException("number is: " + number + ", need >= 2");
        }
        ArrayList<Integer> result = new ArrayList<Integer>();
        int i = 2;
        while (i <= number) {
            if (number % i == 0) {
                result.add(i);
                number /= i;
                i = 2;
                continue;
            }
            ++i;
        }
        return result;
    }

    public static void main(String[] args) throws InterruptedException {
        MathGame game = new MathGame();
        do {
            game.run();
            TimeUnit.SECONDS.sleep(1L);
        } while (true);
    }

    public void run() throws InterruptedException {
        try {
            int number = random.nextInt() / 10000;
            List<Integer> primeFactors = this.primeFactors(number);
            MathGame.print(number, primeFactors);
        }
        catch (Exception e) {
            System.out.println(String.format("illegalArgumentCount:%3d, ", this.illegalArgumentCount) + e.getMessage());
        }
    }

    public static void print(int number, List<Integer> primeFactors) {
        StringBuffer sb = new StringBuffer(number + "=");
        for (int factor : primeFactors) {
            sb.append(factor).append('*');
        }
        if (sb.charAt(sb.length() - 1) == '*') {
            sb.deleteCharAt(sb.length() - 1);
        }
        System.out.println(sb);
    }
}

Affect(row-cnt:1) cost in 760 ms.

3. 方法运行相关指令

watch:方法执行的数据观测

你可以通过watch指令,来监控某个类,监控后,运行下你的功能,复现下场景,arthas会提供给你具体的出参和入参,帮助你排查故障

trace:输出方法调用路径,并输出耗时

这个指令对于优化代码非常的有用,可以看出具体每个方法执行的时间,如果是for循环等重复语句,还能看出n次循环中的最大耗时,最小耗时,和平均耗时,完美!

tt:官方名为时空隧道

这是我调试用的最多的指令,在你对某方法开启tt后,会记录下每一次的调用(你需要设置最大监控次数),然后你可以在任何时候会看这里面的调用,包括出参,入参,运行耗时,是否异常等。非常强大。

4. 线程调试相关指令

thread相关命令:

thread -n:排列出 CPU 使用率 Top N 的线程。

thread -b:排查阻塞的线程

我们代码有时候设计的不好,会引发死锁的问题,卡住整个线程执行,使用这个指令可以轻松的找到问题线程,以及问题的执行语句。

  1. 强大的ognl表达式

众所周知,一般来说,表达式都是调试工具里最强的指令,哈哈。

在Arthas中你可以利用ognl表达式语言做很多事,比如执行某个方法,获取某个信息,甚至进行修改。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[arthas@19856]$ ognl '@com.Arthas@hashSet'
@HashSet[
    @String[count1],
    @String[count2],
    @String[count29],
    @String[count28],
    @String[count0],
    @String[count27],
    @String[count5],
    @String[count26],
    @String[count6],
    @String[count25],
    @String[count3],
    @String[count24],

[arthas@19856]$ ognl  '@com.Arthas@hashSet.add("test")'
@Boolean[true]
[arthas@19856]$
# 查看添加的字符
[arthas@19856]$ ognl  '@com.Arthas@hashSet' | grep test
    @String[test],
[arthas@19856]$

甚至你可以动态更换日志输出级别

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ognl '@com.lz.test@LOGGER.logger.privateConfig'
@PrivateConfig[
    loggerConfig=@LoggerConfig[root],
    loggerConfigLevel=@Level[INFO],
    intLevel=@Integer[400],
]
$ ognl '@com.lz.test@LOGGER.logger.setLevel(@org.apache.logging.log4j.Level@ERROR)'
null
$ ognl '@com.lz.test@LOGGER.logger.privateConfig'
@PrivateConfig[
    loggerConfig=@LoggerConfig[root],
    loggerConfigLevel=@Level[ERROR],
    intLevel=@Integer[200],

]

使用Arthas解决具体问题

1. 响应时间异常问题

工作中遇到一个优化问题,系统中一个导出表格的功能,响应时间长达2分钟,虽然给内部使用,但也不能这么夸张,用trace跟踪下方法,发现是其中的手机号加解密函数占用了非常大的时间,几千个手机号,进行了解密后加密的精彩操作,最终导致了两分钟的返回时间。

2. 某功能Bug导致服务器返回500

首先通过trace看异常报错的方法,之后通过tt排查方法,发现入参进来后,居然走错了方法(因为多态),走到了返回null的方法中,所以导致了NPE空指针错误。

补充

Arthas还支持Web Console,详见:

https://alibaba.github.io/arthas/web-console.html

相似工具

BTrace一是个历史比较久的工具,观察下来Arthas其实和他的理念蛮相似的,相信Arthas也参考过Btrace,作为一个学习样例来开发Arthas。详细的优劣势看图:

其他的相似工具,还有jvm-sandbox,有兴趣的朋友可以去看看。

原理浅谈

分为三个部分:

  • 启动
  • arthas服务端代码分析
  • arthas客户端代码分析

启动

使用了阿里开源的组件cli,对参数进行了解析

com.taobao.arthas.boot.Bootstrap

在传入参数中没有pid,则会调用本地jps命令,列出java进程

进入主逻辑,会在用户目录下建立.arthas目录,同时下载arthas-core和arthas-agent等lib文件,最后启动客户端和服务端

通过反射的方式来启动字符客户端

服务端——前置准备

看服务端启动命令可以知道 从 arthas-core.jar开始启动,arthas-core的pom.xml文件里面指定了mainClass为com.taobao.arthas.core.Arthas,使得程序启动的时候从该类的main方法开始运行。

  • 首先解析入参,生成com.taobao.arthas.core.config.Configure类,包含了相关配置信息
  • 使用jdk-tools里面的VirtualMachine.loadAgent,其中第一个参数为agent路径, 第二个参数向jar包中的agentmain()方法传递参数(此处为agent-core.jar包路径和config序列化之后的字符串),加载arthas-agent.jar包
  • 运行arthas-agent.jar包,指定了Agent-Class为com.taobao.arthas.agent.AgentBootstrap

上图中代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Arthas {

    private Arthas(String[] args) throws Exception {
        attachAgent(parse(args));
    }

    private Configure parse(String[] args) {
        // 省略非关键代码,解析启动参数作为配置,并填充到configure对象里面
        return configure;
    }

    private void attachAgent(Configure configure) throws Exception {
           // 省略非关键代码,attach到目标进程
          virtualMachine = VirtualMachine.attach("" + configure.getJavaPid());
          virtualMachine.loadAgent(configure.getArthasAgent(),
                            configure.getArthasCore() + ";" + configure.toString());
    }


    public static void main(String[] args) {
            new Arthas(args);
    }
}

服务端——监听客户端请求

  • 如果是exit,logout,quit,jobs,fg,bg,kill等直接执行。
  • 如果是其他的命令,则创建Job,并运行。
  • 创建Job时,会根据具体客户端传递的命令,找到对应的Command,并包装成Process, Process再被包装成Job。
  • 运行Job时,反向先调用Process,再找到对应的Command,最终调用Command的process处理请求。

服务端——Command处理流程

  1. 不需要使用字节码增强的命令

其中JVM相关的使用 java.lang.management 提供的管理接口,来查看具体的运行时数据。比较简单,就不介绍了。

  1. 需要使用字节码增强的命令

字节码增加的命令统一继承EnhancerCommand类,process方法里面调用enhance方法进行增强。调用Enhancer类enhance方法,该方法内部调用inst.addTransformer方法添加自定义的ClassFileTransformer,这边是Enhancer类。 Enhancer类使用AdviceWeaver(继承ClassVisitor),用来修改类的字节码。重写了visitMethod方法,在该方法里面修改类指定的方法。visitMethod方法里面使用了AdviceAdapter(继承了MethodVisitor类),在onMethodEnter方法, onMethodExit方法中,把Spy类对应的方法(ON_BEFORE_METHOD, ON_RETURN_METHOD, ON_THROWS_METHOD等)编织到目标类的方法对应的位置。 在前面Spy初始化的时候可以看到,这几个方法其实指向的是AdviceWeaver类的methodOnBegin, methodOnReturnEnd等。在这些方法里面都会根据adviceId查找对应的AdviceListener,并调用AdviceListener的对应的方法,比如before,afterReturning, afterThrowing。

客户端

客户端代码在arthas-client模块里面,入口类是com.taobao.arthas.client.TelnetConsole。

主要使用apache commons-net jar进行telnet连接,关键的代码有下面几步:

  1. 构造TelnetClient对象,并初始化
  2. 构造ConsoleReader对象,并初始化
  3. 调用IOUtil.readWrite(telnet.getInputStream(), telnet.getOutputStream(), System.in, consoleReader.getOutput())处理各个流,一共有四个流:
    • telnet.getInputStream()
    • telnet.getOutputStream()
    • System.in
    • consoleReader.getOutput()

请求时:从本地System.in读取,发送到 telnet.getOutputStream(),即发送给远程服务端。 响应时:从telnet.getInputStream()读取远程服务端发送过来的响应,并传递给 consoleReader.getOutput(),即在本地控制台输出。

关于源码,深入下去还有很多东西需要生啃,我也没有消化得很好,大家可以继续阅读详细资料。

总结

Arthas是一个线上Debug神器,小白也可以轻松上手。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java3y 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Apache中 RewriteRule 规则参数介绍
大家好,又见面了,我是你们的朋友全栈君。Apache中 RewriteRule 规则参数介绍
全栈程序员站长
2022/08/27
12.6K0
Apache 强制 HTTP 全部跳转到 HTTPS
米扑博客最新写了一篇博客《Apache 强制 HTTP 全部跳转到 HTTPS》,分享出来 更多经典技术博客,请见我的米扑博客:https://blog.mimvp.com .htaccess 在每一层独立服务根目录下都存在,例如: 全部网站根目录为   /var/www/html/.htaccess 米扑博客根目录位   /var/www/html/mimvp-wordpress/.htaccess 米扑论坛根目录位   /var/www/html/mimvp-discuz/.htaccess 米扑学习根
阳光岛主
2018/05/17
3.9K0
.htaccess 强制 HTTP 全部跳转到 HTTPS
全站采用https协议访问,所以需要http重定向到https,只需要在.htaccess加入下面规则
星哥玩云
2022/07/16
8850
Apache rewrite Url
本文主要介绍Apache环境下,如何使用mod_rewrite模块完成URL重写功能。
黄啊码
2020/05/31
2K0
Apache 的 ReWrite 的应用
Apache的mod_rewrite是提供了强大URL操作的杀手级 的模块,可以实现几乎所有你梦想的URL操作类型,其代价是你必须接受其复杂性,因为mod_rewrite的主要障碍就是初学者不容易理解和运用,即使 是Apache专家有时也会发掘出mod_rewrite的新用途。 换句话说:对mod_rewrite,或者是打退堂鼓永不再用,或者是喜欢它并一生受用。 ReWrite可以应用在以下方面或者解决以下问题: URL的规划 规范的URL 说明: 在有些网站服务器上,一个资源会拥有多个URL,在实
wangxl
2018/03/07
1.6K0
一些Apache环境常用的.htaccess伪静态规则范例
为了实现网站的SEO优化,或者实现用户需求,会用到一些伪静态规则,下面整理了一些Apache服务器常用的 .htaccess 伪静态规则。 第一、强制www域名301跳转 RewriteEngine on RewriteCond %{HTTP_HOST} ^w3h5\.com [NC] RewriteRule ^(.*)$ https://www.w3h5.com/$1 [L,R=301,NC] 我们将域名替换成自己的域名即可实现非WWW跳转到WWW域名。 第二、强制非WWW域名301跳转 Rewrite
德顺
2019/11/13
2K0
Apache常用配置-运维笔记
===========================.htaccess文件配置笔记=========================== .htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过对.htaccess文件进行设置,可以帮我们实现:网页301重定向、自定义400/403/404/500错误页面、改变文件扩展名、允许/阻止指定IP用户访问、禁止目录列表、配置默认文档等功能,可以说是功能非常强大。
洗尽了浮华
2018/10/22
2.9K0
网站如何实现https重定向(301)到http
对于个人网站站注册比较少的,服务器配置不是很好的,没必要https,https跳转到http是要时间的,会影响网站打开的速度。免费的https每年都要更换。个人博客网站https有一段时间了,而且很多页面都有收录排名,现在已去掉https了,用户搜索从https进网站无法打开页面。去掉后https对面网站有一定的影响,所有这里就要实现访问https自动跳转到http
博客趣
2023/12/12
2.2K1
网站如何实现https重定向(301)到http
个人使用. htaccess伪静态技巧分享
修改.htaccess文件 301跳转重定向 <ifmodule mod_rewrite.c> RewriteEngine On RewriteCond %{HTTP_HOST} xbbai.site$ [NC] RewriteRule ^(.*)$ https://www.xbbai.site/$1 [R=301,L] </ifmodule> 绑定子域名到子目录 <IfModule mod_rewrite.c> RewriteEngine On Rewrit
奶糖味的代言
2018/04/16
2.8K0
网站启用SSL自动301跳转到HTTPS教程
服务器搭建之后就一直在使用宝塔,先抛出安全性能之外,宝塔真的是不错啦,我之前用过许多的控制面板(没办法,技术能力有限,只能用带控制面板的)最好发现还是宝塔比较符合大众的操作习惯,当然收费的amh我也用过,效果不如宝塔,宝塔有很多功能已经集成了,不需要额外的配置文件,比如今天说的启用SSL后,让网站的http自动跳转到https上。如果你的主机不是宝塔,那么参照以下教程试试,也许会有惊喜(记得备份,否则惊喜可能变成惊吓),先说Apache强制301跳转到https教程:
李洋博客
2021/06/15
1.5K0
RewriteCond和13个mod_rewrite应用举例Apache伪静态
大家好,又见面了,我是你们的朋友全栈君。 1.给子域名加www标记 RewriteCond %{HTTP_HOST} ^([a-z.]+)?example.com$ [NC] Rewri
全栈程序员站长
2022/09/06
4.1K0
Apache .htaccess常用方法代码
如何我们网站采用SSL证书,则一般需要强制跳转https路径,这里需要添加强制80端口的跳转。
用户1094633
2022/02/15
1K0
http自动跳转https的配置方法
Apache服务器: 如果需要整站跳转,则在网站的配置文件的标签内,键入以下内容:
await
2021/09/09
1.9K0
apache rewritecond_hfile数据格式中的data字段用于
RewriteCond就像我们程序中的if语句一样,表示如果符合某个或某几个条件则执行RewriteCond下面紧邻的RewriteRule语句,这就是RewriteCond最原始、基础的功能,为了方便理解,下面来看看几个例子。
全栈程序员站长
2022/08/18
4.7K0
Apache URL重写规则
  Apached的重写功能,即是mod_rewrite模块功能,它是apache的一个模块。它的功能非常强大,可以操作URL中的所有部分。
那一叶随风
2018/08/22
3.4K0
Apache URL重写规则
Apache的URL地址重写(RewriteCond与RewriteRule)
大家好,又见面了,我是你们的朋友全栈君。Apache的URL地址重写 http://hi.baidu.com/sonan/blog/item/c408963d89468208bba16716.html 第一种方法:Apache环境中如果要将URL地址重写,正则表达式是最基本的要求,但对于一般的URL地址来说,基本的匹配就能实现我们大部分要求,因此除非 是非常特殊的URL地址,但这不是我要讨论的范围,简单几招学会Apache中URL地址重写,通过实例展示,轻松学会URL地址重写: URL实例
全栈程序员站长
2022/06/30
2.7K0
apache如何设置http自动跳转到https
apache环境下,配置好https后,需要设置url重定向规则,使网站页面的http访问都自动转到https访问。
WindWant
2020/09/11
3.7K0
SEO分享:彻底禁止搜索引擎收录非首选域名的方法
众所周知,绝大多数网站都会有一个首选域名,从用户体验考虑,通常站长们还会另外解析一个域名,并设置 301 重定向。例如,用户未输入 www 的时候,仍然可以访问到我们的网站,就像访问 http://b
张戈
2018/03/23
4.8K0
PHP扩展模块、Apache之rewrite模块
说明: 在配置文件中加入“extension=redis.so”即可!该文件中“;”表示注释符号,同“#”。
阿dai学长
2019/04/03
2.5K0
Nginx/Apache之伪静态设置 - 运维小结
一、什么是伪静态 伪静态即是网站本身是动态网页如.php、.asp、.aspx等格式动态网页有时这类动态网页还跟"?"加参数来读取数据库内不同资料,伪静态就是做url重写操作(即rewrite)。很典
洗尽了浮华
2019/05/25
13.5K0
相关推荐
Apache中 RewriteRule 规则参数介绍
更多 >
LV.1
这个人很懒,什么都没有留下~
目录
  • 前言
  • 线上Debug神器Arthas
    • Arthas使用实例
      • 快速启动
      • 基本使用
    • 使用Arthas解决具体问题
      • 补充
    • 相似工具
    • 原理浅谈
      • 启动
      • 服务端——前置准备
      • 服务端——监听客户端请求
      • 服务端——Command处理流程
      • 客户端
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档