作者 | Agentd
Arthas watch 命令使⽤指南
Arthas 是我很喜欢的⼀款 Java 领域的开发调试⼯具。
每次测试遇到问题的时候,当别⼈为了加⼀条⽇志⽽重发代码,我都会欣慰地拿出我的 Arthas 并且告诉他们:少年,你不⽤再为了加⽇志就重发代码⽽烦恼了。Arthas,你值得拥有。
这次我要介绍的是我使⽤最多的⼀个功能:watch。Arthas 功能虽多,但我最喜欢的还是这⼀个。使⽤ watch 之后,我再也不⽤为了观察函数调⽤⽽加⽇志了。
Arthas 是什么
官⽹是这么介绍⾃⼰的:
Arthas 是 Alibaba 开源的 Java 诊断⼯具,深受开发者喜爱。 当你遇到以下类似问题⽽束⼿⽆策时,Arthas 可以帮助你解决:1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?2. 我改的代码为什么没有执⾏到?难道是我没 commit?分⽀搞错了?3. 遇到问题⽆法在线上 debug,难道只能通过加⽇志再重新发布吗?
4. 线上遇到某个⽤户的数据处理有问题,但线上同样⽆法 debug,线下⽆法重现!5. 是否有⼀个全局视⾓来查看系统的运⾏状况?6. 有什么办法可以监控到 JVM 的实时运⾏状态?7. 怎么快速定位应⽤的热点,⽣成⽕焰图?
⼀键安装并启动 Arthas
⽅式⼀:通过 Cloud Toolkit 实现 Arthas ⼀键远程诊断
Cloud Toolkit 是阿⾥云发布的免费本地 IDE 插件,帮助开发者更⾼效地开发、测试、诊断并部署应⽤。通过插件,可以将本地应⽤⼀键部署到任意服务器,甚⾄云端(ECS、EDAS、ACK、ACR 和 ⼩程序云等);并且还内置了 Arthas 诊断、Dubbo⼯具、Terminal 终端、⽂件上传、函数计算 和 MySQL 执⾏器等⼯具。不仅仅有 IntelliJ IDEA 主流版本,还有 Eclipse、Pycharm、Maven 等其他版本。
⽅式⼆:直接下载
curl -O https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar复制代码
稍微解释⼀下上⾯这条 shell 命令。命令分为两部分,&& 之前的部分是下载 Arthas,之后的部分是启动 Arthas。
你可能会疑惑下载⽂件为什么不⽤ wget ⽽是⽤ curl?这是因为有些服务器是没有预装 wget 的,但是基本都预装了 curl。如果你的服务器预装了 wget 的话完全可以把 'curl' 改成 wget。如果使⽤ wget 的话命令可以改成:
# wget 版命令
wget https://alibaba.github.io/arthas/arthas-boot.jar && java -Dfile.encoding=UTF-8 -jar arthas-boot.jar复制代码
另外⼀个需要解释的点是 -Dfile.encoding=UTF-8,这个 Java 设置是为了让 Arthas 输出中⽂的时候不会乱码,这⼀点可以看⼀下我以前的⽂章。
Arthas watch 命令
watch 让你能⽅便地观察到指定⽅法的调⽤情况。能观察到的范围为:返回值、抛出异常、⼊参(还能观察执⾏函数的对象本⾝,不知道为什么
官⽅介绍的时候没说这个」,通过编写 OGNL 表达式进⾏对应变量的查看。
# watch -h# USAGE
watch [-b] [-e] [-x 1. 观察⽅法返回结果 returnObj 使⽤⽅式看着复杂,其实很简单。来个最简单的⽰例: 假设我们要观察下⾯这段代码中字符串的 contains ⽅法。 public class App { public static void main(String[] args) throws IOException { String hello = \"Hello Arthas\"; while (true) { boolean contains = StringUtils.contains(hello, \"Arthas\"); System.out.println(contains); } }} 复制代码 可以使⽤如下语句: ## 观察 contains 返回结果 [arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains returnObj -n 3# Press Q or Ctrl+C to abort. # Affect(class-cnt:1 , method-cnt:2) cost in 68 ms. # ts=2020-05-02 16:46:04; [cost=2.424254ms] result=@Boolean[true]# ts=2020-05-02 16:46:05; [cost=0.21033ms] result=@Boolean[true]# ts=2020-05-02 16:46:06; [cost=0.165514ms] result=@Boolean[true]复制代码 -n 3 表⽰只执⾏三次,这参数挺常⽤,不然很容易被输出刷屏。 2. 过滤不关⼼的调⽤ condition-express 显然,真实的案例肯定不会如上⾯的⽰例那么简单。 真实的服务代码中,肯定不⽌⼀个地⽅调⽤了 String 的 contains ⽅法。我们需要把⽆关的调⽤过滤掉。 ## 观察 contains 返回结果,并且过滤掉⽆关调⽤ [arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains returnObj 'params[1]==\"Arthas\"'# Press Q or Ctrl+C to abort. # Affect(class-cnt:1 , method-cnt:2) cost in 29 ms. # ts=2020-05-02 16:48:50; [cost=0.331109ms] result=@Boolean[true]# ts=2020-05-02 16:48:51; [cost=0.175224ms] result=@Boolean[true]# ts=2020-05-02 16:48:52; [cost=0.138984ms] result=@Boolean[true]复制代码 ⼊参是⼀个很容易把不同调⽤区分开的⽅法,通过 params[1]==\"Arthas\" 这个 condition-express,我们可以只保留第⼆个⼊参是 Arthas 的函数调⽤。 3. 同时观察⼊参和结果 [arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]==\"Arthas\"'# Press Q or Ctrl+C to abort. # Affect(class-cnt:1 , method-cnt:2) cost in 33 ms. # ts=2020-05-02 16:51:27; [cost=0.507486ms] result=@ArrayList[# @Object[][isEmpty=false;size=2],# @Boolean[true],# ] 复制代码 通过 {} 把字段包起来,可以同时观察想观察的字段。可以注意到⼀个点,params 是⼀个数组,但是打印 params 的时候并没有把具体内容打印出来,这个时候可以使⽤ -x 2 来指定打印对象的属性遍历深度。 arthas@11939]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]==\"Arthas\"' -x 2# Press Q or Ctrl+C to abort. # Affect(class-cnt:1 , method-cnt:2) cost in 35 ms. # ts=2020-05-02 16:51:33; [cost=0.391218ms] result=@ArrayList[# @Object[][ # @String[Hello Arthas],# @String[Arthas],# ], # @Boolean[true],# ] 复制代码 4. 给⼤家来⼏个我实际⽤到的例⼦ 在陌陌做动态推荐开发的时候,测试时经常会遇到查看某个⽤户是否开启了相应的业务开关,经常就会需要查看某个实验开关是否开启。 ## 查看陌陌⽤户 1234567 是否开启 ElasticSearch 开关 watch com.momo.Experiment enableElasticSearch returnObj 'target.momoId==\"1234567\"'# ts=2020-05-02 20:09:46; [cost=24.443527ms] result=@Boolean[true]复制代码 我还经常会根据⼊参的陌陌⽤户 id 进⾏判断,查看返回结果或者异常: ## 查看 MorecControlFlow 类的 process ⽅法返回结果 ## process ⽅法第⼀个参数的 momoId 属性值为 “123454567” 才⽣效## 类路径和陌陌号都⾮真实数据 watch com.momo.MorecControlFlow process returnObj 'params[0].momoId==\"123454567\"'# ts=2019-03-18 21:09:46; [cost=264.434972ms] result=@Boolean[true]## 查看 IMorecShuffler 类的 shuffle 抛的异常 ## process ⽅法第⼀个参数的 momoId 属性值为 “123454567” 才⽣效 watch com.momo.plugins.shuffler.IMorecShuffler shuffle throwExp 'params[0].morecRequest.momoId==\"123454567\"'# ts=2019-03-27 20:54:29; [cost=46.642339ms] result=java.lang.IndexOutOfBoundsException: Index: 12, Size: 11 at java.util.ArrayList.rangeCheckForAdd(ArrayList.java:665) at java.util.ArrayList.add(ArrayList.java:477) at com.momo.plugin.shuffler.RoomShuffler.shuffle(RoomShuffler:45)复制代码 ⼀些⼩提⽰ 上⾯我只是列了⼀下常⽤的观察⽅式和参数,watch ⽀持的命令还有很多,你可以查看 Arthas 的 watch 命令。还可以通过启动 arthas 命令之后使⽤ watch -h 查看。 使⽤ Arthas 的过程中很多⼈会觉得获取类的全限定名很费劲,其实这个可以通过 Idea 的 Copy Refrence 快捷键解决。我⾃⼰定义的快捷键是⌥⇧⌘C。 还有⼀点就是写代码的时候最好把代码拆细,尽量把⼩功能也封装成单独的函数,等你需要使⽤ Arthas 观察函数调⽤的时候,你会回来感谢⾃⼰的。 Arthas 征⽂活动⽕热进⾏中 Arthas 官⽅正在举⾏征⽂活动,如果你有: 使⽤ Arthas 排查过的问题对 Arthas 进⾏源码解读对 Arthas 提出建议 不限,其它与 Arthas 有关的内容欢迎参加征⽂活动,还有奖品拿哦~ “关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原⽣流⾏技术趋势、云原⽣⼤规模的落地实践,做最懂云原⽣开发者的公众号。” 因篇幅问题不能全部显示,请点此查看更多更全内容