大纲目录
这篇文章是之前学习Arthas和jvm-sandbox的一些心得和总结,希望能帮助到大家。本文字较多,可以根据目录进行对应的阅读。
背景:现在的问题所在?
Arthas: Arthas能帮助你干什么?各种命令原理是什么?
jvm-sandbox: jvm-sandbox能帮助你干什么?
实现原理?自己如何实现一个?
常见的一些问题?
1.背景
2018年已过,但是在过去的一年里面开源了很多优秀的项目,这里我要介绍两个比较相似的阿里开源项目一个是Arthas,另一个是jvm-sandbox。这两个项目都是在今年开源的,为什么要介绍这两个项目呢?这里先卖个关子,先问下大家不知道是否遇到过下面的场景呢?
当你线上项目出了问题,但是一打开日志发现,有些地方忘记打了日志,于是你马上补上日志,然后重新上线。这个在一些上线流程不规范的公司还比较轻松,在一些流程比较严格,比如美团上线的时候就有封禁期,一般就只能9点之后才能上线。有可能这样一拖就耽误了解决问题的黄金时刻。
当你的项目某个接口执行速度较慢,为了排查问题,于是你四处加上每个方法运行时间。
当你发现某个类有冲突,好像在线上运行的结果和你预期的不符合,手动把线上编译出的class文件下载下来然后反编译,看看究竟class内容是什么。
当代码已经写好准备联调,但是下游业务环境并没有准备好,于是你把以前的代码依次进行注释,采用mock的形式又写了一遍方便联调。
以上这些场景,再真正的业务开发中大家或多或少都遇见过,而一般大家的处理方式和我在场景的描述得大体一致。而这里要给大家介绍一下Arthas和jvm-sandbox,如果你学会了这两个项目,上面所有的问题再你手上再也不是难事。
2. Arthas
当然再介绍Arthas之前还是要给大家说一下Greys,无论是Arthas还是jvm-sandbox都是从Greys演变而来,这个是2014年阿里开源的一款Java在线问题诊断工具。而Arthas可以看做是他的升级版本,是一款更加优秀的,功能更加丰富的Java诊断工具。
这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
是否有一个全局视角来查看系统的运行状况?
有什么办法可以监控到JVM的实时运行状态?
下面我将会介绍一下Arthas的一些常用的命令和用法,看看是如何解决我们实际中的问题的,至于安装教程可以参考Arthas的github。
2.1 奇怪的类加载错误
相信大家都遇到过NoSuchMethodError这个错误,一般老司机看见这个错误第一反应就是jar包版本号冲突,这种问题一般来说使用maven的一些插件就能轻松解决。
之前遇到个奇怪的问题,我们有两个服务的client-jar包,有个类的包名和类名均是一致,在编写代码的时候没有注意到这个问题,在编译阶段由于包名和类名都是一致,所有编译阶段并没有报错,在线下的运行阶段没有问题,但是测试环境的机器中的运行阶段缺报出了问题。这个和之前的jar包版本号冲突有点不同,因为在排查的时候我们想使用A服务的client-jar包的这个类,但是这个jar包的版本号在Maven中的确是唯一的。
这个时候Arthas就可以大显神通了。
2.1.1 sc命令
找到对应的类,然后输出下面的命令(用例使用的是官方提供的用例):
$ sc -d demo.MathGame
class-info demo.MathGame
code-source /private/tmp/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@3d4eac69
+-sun.misc.Launcher$ExtClassLoader@66350f69
classLoaderHash 3d4eac69
Affect(row-cnt:1) cost in 875 ms.
可以看见打印出了code-source,当时发现了code-source并不是从对应的Jar包取出来的,于是发现了两个服务对于同一个类使用了同样的包名和类名,导致了这个奇怪的问题,后续通过修改包名和类名进行解决。
sc原理
sc的信息主要从对应的Class中获取。
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/088e68b07d8863e025f615c827b8a735.jpeg)
对于我们上面的某个类从哪个jar包加载的是通过CodeSource来进行获取的:
![640?wx_fmt=png](https://i-blog.csdnimg.cn/blog_migrate/4a1004da0326eac783460f949578b1cd.png)
2.1.2 jad
Arthas还提供了一个命令jad用来反编译,对于解决类冲突错误很有用,比如我们想知道这个类里面的代码到底是什么,直接一个jad命令就能搞定:
$ jad java.lang.String
ClassLoader:
Location:
/*
* Decompiled with CFR 0_132.
*/
package java.lang;
import java.io.ObjectStreamField;
...
public final class String
implements Serializable,
Comparable<String>,
CharSequence {
private final char[] value;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
public String(byte[] arrby, int n, int n2) {
String.checkBounds(arrby, n, n2);
this.value = StringCoding.decode(arrby, n, n2);
}
...
一般通过这个命令我们就能发现和你所期待的类是否缺少了某些方法,或者某些方法有些改变,从而确定jar包冲突。
jad原理
jad使用的是cfr提供的jar包来进行反编译。这里过程比较复杂这里就不进行叙述。
2.2 动态修改日志级别
有很多同学可能会觉得动态修改日志有什么用呢?好像自己也没怎么用过呢?
一般大家日志级别默认是info,有时候需要查看debug的日志可能需要重新上线。
当线上某个应用流量比较大的时候,如何业务出现问题,可能会短时间之内产生大量日志ÿ