1、Arthas简介

Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,目的是提供一种能快速有效的对线上java应用的诊断工具。

2、接入前的提示

对于Arthas的引入其实是慎重的,我们一方面非常兴奋它给我们带来了一系列的黑科技帮助我们即时追踪链路诊断,一方面又纠结该工具使用的字节码拦截技术带来的一个是性能消耗、可被入侵的字节码现实修改技术而担忧。

Arthas也提供了几种方式给我们选择去接入并使用它,本文详细讲讲,接入Arthas的一些实现方案,也给学习此技术的同学一些参考或其他可能性。

3、接入方式

接入Arthas目前有三种方案

序号 方式 备注
1 arthas-boot.jar 官方推荐
2 arthas-agent.jar 可选
3 arthas-spring-boot-starter 依赖 可选

1) arthas-boot.jar

下载arthas-bin.zip(github上大家去下载最新的即可),解压后得到全量的Arthas文件,然后用java -jar arthas-boot.jar的方式启动:

还可以使用解压后的as.sh文件进行启动,直接在 shell 下面执行./as.sh,就会进入交互界面。

此方式也是官方推荐方式,因为此方式是而且启动一个进程,使用的是VirtualMachine.attach()技术粘附在指定的可选的java进程中的,如果你的机器启动了很多个java进程,根据进程号进入到目标应用去使用,不使用后可以选择退出。因为attach到指定进程是通过Instrumentation来拦截所有的字节码,关键点是此方式在想用Arthas的时候才会去启动它并拦截字节码,不用就退出,对平时没有使用的时候是没有任何性能损耗的,只有用的时候才会额外有资源耗损。

2) arthas-agent.jar

在全量的 arthas zip 包中存在arthas-agent.jar文件,此文件就是给使用者以javaagent的方式使用Arthas, -javaagent:/j具体路径/arthas-agent.jar来启动:

## 示例
java -javaagent:/tmp/arthas/arthas-agent.jar -jar agent-demo.jar

方式一

默认的配置项在解压目录里的arthas.properties文件里。


## 注意这里这两个端口,启动你访问http://lcoalhost:3658 或者 http://lcoalhost:8563 都会出现一个arthas的页面版给你使用Arthas。
## 研究过arthas-tunnel-server可能会非常熟悉,就是少了agent_id你去填并且执行Connect。
## 其实你只要使用一个即可,把其中一个关闭了就可以了,关闭就是把值改成:arthas.telnetPort=-1
arthas.telnetPort=3658
arthas.httpPort=8563
arthas.ip=127.0.0.1

# seconds
arthas.sessionTimeout=1800
arthas.localConnectionNonAuth=true
# 以下配置先省略。。。。。。

image-1720253669523

方式二

这种方式还延伸出另外一种使用方式:这里就要引出arthas-tunnel-server这个页面版应用了。其实就是利用arthas-agent.jar提供的一个注册到控制台的方式,开放出tunnel端口出来给控制台去连接,连接成功后就跟使用本地上面那个页面一样的效果。

Arthas tunnel server 是一个 spring boot fat jar 应用,此应用需要额外部署出来,等同于一个中心节点通过各个机器上的java进程暴露的tunnel端口进行访问使用。直接java -jar启动:

java -jar  arthas-tunnel-server.jar

默认情况下,arthas tunnel server 的 web 端口是8080,arthas agent 连接的端口是7777。启动之后,可以访问http://127.0.0.1:8080/ ,再通过agentId连接到已注册的 arthas agent 上。

再回到这个arthas.properties配置文件上


## 我们把下面这两个端口都屏蔽掉,因为你都要去tunnel server控制台中使用了,这个本地的就没必要开放出来了。
arthas.telnetPort=-1
arthas.httpPort=-1
arthas.ip=127.0.0.1

## 把这三个配置打开,此配置就是注册到控制台的主要配置。
## appName就不说了,跟应用相关的同名即可
arthas.appName=agent-demo
## 这个就是你部署的控制台的地址,自己按实际填写即可
arthas.tunnelServer=ws://127.0.0.1:7777/ws
## 这个也是关键,这个ID要全局唯一,建议用appName加机器ip的方式来设置,原因你在docker上部署的java应用仔细琢磨一下就知道了。
arthas.agentId=agent-demo-127.0.0.1

# 以下配置先省略。。。。。。

启动java应用成功后在tunnel-server的控制台日志就能看到java应用注册上来的日志

2024-07-06 16:38:15.221  INFO 22184 --- [Server-boss-1-1] 
io.netty.handler.logging.LoggingHandler  : [id: 0x57318fbe, L:/0:0:0:0:0:0:0:0:7777] READ: [id: 0x7648bbca, L:/127.0.0.1:7777 - R:/127.0.0.1:64746]
2024-07-06 16:38:15.221  INFO 22184 --- [Server-boss-1-1] 
io.netty.handler.logging.LoggingHandler  : [id: 0x57318fbe, L:/0:0:0:0:0:0:0:0:7777] READ COMPLETE
2024-07-06 16:38:15.256  INFO 22184 --- [ver-worker-3-13] 
c.a.a.t.server.TunnelSocketFrameHandler  : websocket handshake complete, uri: /ws?method=agentRegister&arthasVersion=3.7.2&appName=agent-demo&id=agent-demo-127.0.0.1

通过控制台页面去连接java进程就得输入咱们指定的agentId了,这也是tunnel控制台和本地控制台的区别本地都不用输入agentId

image-1720255379935

最后说说agent的方式为什么不是官方推荐的?因为agent在启动的时候就已经随进程一起被加载,agent的作用就是拦截所有的字节码流量进行解析的,你用与不用它都在消化一定的资源,虽然说这个点资源可以忽略,但是有部分人群会觉得它冒犯了TA自己(哈哈哈哈哈哈)。

3) arthas-spring-boot-starter

这种方式和agent其实差不多,就是在启动引用的时候从依赖包中解压出arthas-core.jar文件来,触发了com.taobao.arthas.core.Arthas类。这个类中只有一个main方法,那这个方法具体是怎么触发的呢?这大概得讲到main方法的另一种加载方式,就是通过jar中的描述符文件META-INF.MANIFEST.MF,大家去看一眼这个core包里面的这个文件,里面有个Main-Class: com.taobao.arthas.core.Arthas就是描述了这个你要加载我这个包,就会触发加载这个类的main方法,这个方法就是做了一件事,上面我也提过的:VirtualMachine.attach。而arthas-spring-boot-starter就是启动时而且加载到了arthas-core.jar到内存中,所有默认都会触发到这个main方法,包括arthas-agent方式。

不过arthas-spring-boot-starter方式只能用于和arthas-tunnel-server控制台的连接才能使用,所以使用该starter依赖那就必须配置那三样属性。

请看一个简单的spring-boot应用中的application.properties文件配置

server.port=8090

# spring.arthas.enabled = true
spring.application.name = spring-boot-demo
arthas.app-name= = ${spring.application.name}
arthas.agent-id = spring-boot-demo-127.0.0.1
arthas.tunnel-server = ws://127.0.0.1:7777/ws

当控制台日志中打印如下信息,也就是证明我们启动成功并注册到位了

2024-07-06 17:02:29.117  INFO 22184 --- [Server-boss-1-1] 
io.netty.handler.logging.LoggingHandler  : [id: 0x57318fbe, L:/0:0:0:0:0:0:0:0:7777] READ: [id: 0xf7a9e3bd, L:/127.0.0.1:7777 - R:/127.0.0.1:53318]
2024-07-06 17:02:29.118  INFO 22184 --- [Server-boss-1-1] 
io.netty.handler.logging.LoggingHandler  : [id: 0x57318fbe, L:/0:0:0:0:0:0:0:0:7777] READ COMPLETE
2024-07-06 17:02:29.145  INFO 22184 --- [rver-worker-3-1] 
c.a.a.t.server.TunnelSocketFrameHandler  : websocket handshake complete, uri: /ws?method=agentRegister&arthasVersion=3.7.2&appName=%3D%20spring-boot-demo&id=spring-boot-demo-127.0.0.1

image-1720256641816

VirtualMachine.attach是一个启用了一个jvm进程,找到需要attach的jvm进程,让它加载agentMain,那么agentMain就会被加载到对方jvm执行。arthas就是使用这种方式attach进jvm进程,开启一个socket然后进行目标jvm的监控。

额外添加一个小知识:如何获取一个类里面的非静态属性值,官网中的示例只有静态的属性值如何获取,如果我们有个值用的是@Value("${mysql.password:xxxxxx}")的写法,有时候通过第三方配置中心动态依赖的值不太确定有没有更新正确,我们要检查的时候可以用下面的这个语法

[arthas@45672]$ vmtool -x 3 --action getInstances --className 你的全类名称 --express 'instances[0].你的属性名'

打印:

@String[xxxxxx]

4、最后

我们了解了三种方式的接入,并且利弊也了解到了,我们要使用Arthas这个工具也对它了解又深入了一点,但我们在实际的上生产的过程中对机器来讲又有不同的挑战。

生产上的机器不可能通过Arthas直接启动java -jar arthas-boot.jar给你命令权限,那你只能想到通过web页面去操作,但是web页面也不可能给你每个java节点暴露IP、端口给你访问。那就只能通过arthas-tunnel-server控制台来解决,大家都往这个控制台注册。但有给我们带来另外一个问题:agentId

如果我们使用的是虚拟机技术,我们可以通过一些手段在部署的机器上注册agentId的时候拿到机器的IP作为agentId。但我们大多数使用的是Docker技术,可能这个时候你用的IP重启后IP变了,IP给别的应用节点,那你现在注册如果只是用IP那可能就连接到不对的应用上,所以上面我说建议用应用+IP的方式设置为agentId,这样只是非常小的概率会注册错。

更多的方式由各位去亲自体验体验,本章只讲和Arthas接入,并没有具体的使用命令的详解,下期再见。

下一篇