通过比较作者2011年左右开发的一个简单的 Java Troubleshooting 工具 simple-profiler 与神器 greys-anatomy 的差距,
知差距而后勇
那是2011年(或者是 2012 年?), 看到 @bluedavvy 大神推荐 BTrace,跟进学习了一下才发现的确是神器一枚。小试牛刀,解决了一个线上问题,但总感觉写 BTrace 脚本有些门槛,尝试在公司内部推广结果效果不好。本着不重写一遍神器就体会不到神器究竟有多牛
的指导思想,撸起袖子写了一个简易版的工具:simple-profiler
。
时隔接近4年, 偶然发现了神器 greys-anatomy,才知道自己的差距,决定写篇文章对比一下,整理一下思路。
基础
Java Attach API 是一个提供可以挂到另一个 Java 虚拟机的扩展,通过 Attach API 可以直接将自己的代码挂载到另一个 Java 虚拟机中运行, 比如: 我可以在写一段 Java 程序监听 8080 端口的 HTTP 协议, 通过 Attach API 挂载到另一个没有监听8080端口的 Java 进程
Java Instrumentation 允许 Java 程序通过 Agent 来检测已经 Java 程序的运行,可以通过重写 Java bytecode 的方式, 对现有的类进行替换或者改写。比如如下代码:
public class Test {
public void work(){
doWork();
}
}
可以被改写成:
public class Test {
public void work(){
final long startTs = System.currentTimeMillis();
try {
doWork();
} finally{
final long duration = System.currentTimeMillis() - startTs;
System.out.println("work duration: " + duration);
}
}
}
那如何实现 Java Bytecode 的改写呢? 这就不得不提到另一个神器: ASM
-
ASM
众所周知, Java 编译器将 .java 文件编译成 .class 文件。.class 遵循 Java Virtual Machine Specification。 既然有了规范,就可以有 Library 可以去解析 .class 文件。 ASM 就是这样一个 Java Library, 用来解析Java 的 bytecode, 也就是 .class 文件。
不过说实话, 如果不对着 JVM 规范,用 ASM 解析字节码很痛苦。好消息是同样有牛人看不下去了,开发了一个新的 Library: ByteBuddy, 简化了语义,比如:
1. Class<?> dynamicType = new ByteBuddy()
2. .subclass(Object.class)
3. .method(ElementMatchers.named("toString"))
4. .intercept(FixedValue.value("Hello World!"))
5. .make()
6. .load(getClass().getClassLoader())
7. .getLoaded();
8.
9. assertThat(dynamicType.newInstance().toString(), is("Hello World!"));
有兴趣的同学可以用 ASM 实现同样的功能对比一下。
简称 JMX, 简单概括来说, JMX 就是一套可以在运行时获取大多数 JVM 状态的接口,比如 GC、线程、ClassLoading 信息等
- 几个有用的 Linux 命令
- pidstat: 用来获取进程的 IO 和 CPU 占用信息
- top: 老牌工具,最近有新兴工具 htop、iotop 等
基础啰嗦完, 我们从安装、方法拦截、简化其他工具三个方面来对比simple-profiler 和 greys-anotomy。
安装
simple-profiler
simple-profiler 是参照 BTrace, 因此跟 BTrace 一样, 需要下载对应的 tar 包放到目标服务器上解压缩
greys-anotomy
一行 bash 脚本, 自动下载最新的包并安装。
curl -sLk http://ompc.oss.aliyuncs.com/greys/install.sh|sh
安装过程对比结论:
- simple-profiler 简直就是石器时代
- greys-anotomy 才是先进生产力代表
拦截 Java 方法
simple-profiler 拦截
支持两种方式:改写一层 Java 字节码获取方法耗时(我起名叫: simple profile 模式)和改写 N 层字节码获取某方法 N 层调用栈每个方法的耗时(我起名叫: detail profile 模式)。
比如你怀疑 Test 类有性能问题, 通过 simple profile 方式, 发现 Test 类中的 work
方法非常耗时。如果想进一步知道 work
方法中调用了哪个方法导致了耗时很长, 就需要通过 detail profile 模式,将 work
方法调用的所有的其他类的方法改写字节码, 统计耗时, 找出真凶。
实现方式很简单:
- simple profile 模式下, 仅仅找到 Test 类的源代码,将所有方法改写, 统计耗时
- detail 模式下,有些麻烦。通过解析 Test 类的
work
方法, 迭代方式直至到达第 N 层,将所有涉及的类的方法改写,并且在 ThreadLocal 中使用标记, 只有上层是 Test 类的work
方法时才统计时间,避免误判。
greys-anotomy 拦截
不仅仅支持 simple-profiler 的所有用过,还支持参数解析,也就是说支持仅仅在参数 ==
某些值的时候才进行统计, 非常实用。
不仅如此,还可以通过 tt
命令,记录调用历史,供后续分析。这个功能非常实用,在 troubleshooting 时很有可能满足条件的调用转瞬即逝, 一旦没有留下现场,再等一次那才忧伤。
方法拦截对比结论:
还用说么, 肯定是 greys-anotomy 强大到一塌糊涂啊
封装其他工具
simple-profiler
- 封装了 jstack 和 pidstat, 可以直接查看 IO 消耗最大的线程的 stacktrace
- 封装了 jstack 和 top,直接查看 CPU 占用最高的线程的 stacktrace
- 封装了 jmap,直接统计内存占用信息
- 封装了 jinfo,直接查看 JVM 信息
greys-anotomy
貌似没有封装任何其他工具。
封装工具总结:
- 貌似是 simple-profiler 略胜一筹,但不是 simple-profiler 做的更好, 而是 greys-anotomy 的作者根本就没想做这层封装。
交互方式
simple-profiler
- 使用 server-client 模式, server 是一个 web 程序
- 客户端通过
server_url
指定 server 地址,attach 成功后自动注册到 server - 用户通过 web 页面跟 JVM 交互
greys-anotomy
greys-anotomy- 仅仅支持命令行,roadmap 中说 2.x.x 会支持图形客户端
- 支持远程操作
交互方式总结
- simple-profiler 虽然支持网页操作,但页面实在做的太烂,并且没有历史回溯功能,非常简陋
- greys-anotomy 虽然仅仅有命令行,但用户都是工程师,并无大碍
总结
-
神器 greys-anatomy 值得所有 Java 程序员拥有,没事翻翻它的源码,也一定受益匪浅。
-
我写的 simple-profiler 已经落灰,但写的过程中也学些到很多知识。还是那个原则:
好东西不重写一遍永远不知道好东西有多好,当年的 "Hello World" 难道你没重写么?
最后, 贴几行 simple-profiler 的log,纪念那些个无眠的夜……
profiler-client bash bin/profiler_run.sh --pid 68189
SimpleProfiler [2016-08-14 13:45:44.255] [INFO] attach cost(ms): 321
SimpleProfiler [2016-08-14 13:45:44.453] [INFO]
Simple Profiler started at 192.168.0.111:8030
-- EOF --
网友评论