上一篇文章 中通过对一次线上事故的复盘,引出了福报厂的Arthas,一个建立在Java动态字节码技术之上的Java诊断工具;关于Arthas的使用方式就不赘述了,查看官方文档可以很快上手,玩法也特别多;上一篇中也仅仅只介绍了一种使用场景,即”debug线上JVM内部class信息、在线watch方法执行并查看方法输入输出、在线反编译class、重新编辑Java后直接热部署“的组合拳(手动狗头)…
上手一门技术最基本要做到 what-how-why,在知道了Arthas是什么(what),以及如何使用(how)之后,自然需要去了解它内如如何工作的,以及底层原理(why);而了解底层原理最直接的方式便是阅读源码,毕竟代码不会骗人的,但任何技术文章或书籍都会有”噪音“,那么接下来就从Arthas源码入手,聊聊Java Agent、Instrument、动态字节码技术;
一直都回避写源码方向的文章,源码读起来容易,但写解读就需要粘贴大量代码,会占据大量篇幅,并且很容易写成流水账,但像Arthas或者Dubbo这类优秀的国产项目还是值得的;
同时由于时间与精力有限,本篇着重介绍Java Agent,Instrument等动态字节码技术放到下一篇…
Arthas源码
记得当初第一次拿到Arthas源码的时候,印象最深的就是那可爱的TODO.md
,哈哈,像是某位福娃老哥在做交接…
* 代码还是很乱,需要继续重构
* 依赖需要清理,几个问题:
* 所有 apache 的 common 库应当不需要
* json 库有好几份
* `jopt-simple` 看下能不能用 `cli` 取代
* `cli`, `termd` 的 artifactId, version 需要想下。是不是应该直接拿进来。他们的依赖也需要仔细看一下
* termd 依赖 netty,感觉有点重,而且第一次 attach 比较慢,不确定是 netty 的问题还是 attach 的问题
* 目前 web console 依赖 termd 中自带的 term.js 和 css,需要美化,需要想下如何集成到研发门户上
* 因为现在没有 Java 客户端了,所以 batch mode 也就没有了
* `com.taobao.arthas.core.shell.session.Session` 的能力需要和以前的 session 的实现对标。其中:
* 真的需要 textmode 吗?我觉得这个应该是 option 的事情
* 真的需要 encoding 吗?我觉得仍然应该在 option 中定义,就算是真的需要,因为我觉得就应该是 UTF-8
* duration 是应当展示的,session 的列表也许也应当展示
* 需要仔细看下 session 过期是否符合预期
* 多人协作的时候 session 原来是在多人之间共享的吗?
* 所有的命令现在实现的是 AnnotatedCommand,需要继续增强的是:
* Help 中的格式化输出被删除。需要为 `@Description` 定义一套统一的格式
* 命令的输入以及输出的日志 (record logger) 被删除,需要重新实现,因为现在是用 `CommandProcess` 来输出,所以,需要在 `CommandProcess` 的实现里打日志
* `com.taobao.arthas.core.GlobalOptions` 看上去好奇怪,感觉是 OptionCommand 应当做的事情
* `com.taobao.arthas.core.config.Configure` 需要清理,尤其是和 http 相关的
* 需要合并 develop 分支上后续的修复
* 代码中的 TODO/FIXME
回归主题,首先直奔 arthas-core
模块,因为这是整个arthas的入口,即执行java -jar arthas-core.jar
;主函数内最重要的任务就是通过java进程的pid来 attach JVM 与 load agent;
private void attachAgent(Configure configure) throws Exception {
VirtualMachineDescriptor virtualMachineDescriptor = null;
for (VirtualMachineDescriptor descriptor : VirtualMachine.list()) {
String pid = descriptor.id();
if (pid.equals(Long.toString(configure.getJavaPid()))) {
virtualMachineDescriptor = descriptor;
break;
}
}
VirtualMachine virtualMachine = null;
try {
if (null == virtualMachineDescriptor) {
// 使用 attach(String pid) 这种方式
virtualMachine = VirtualMachine.attach("" + configure.getJavaPid());
} else {
virtualMachine = VirtualMachine.attach(virtualMachineDescriptor);
}
//略
virtualMachine.loadAgent(arthasAgentPath, configure.getArthasCore() + ";"