不會還有人不知道 Arthas 可以條件過濾進行 Watch 吧?
前言
Arthas 的 watch 指令一直是我排查線上問題時使用最多的指令,沒有之一。而按照條件進行 watch 也是很常見的一個需求,例如線上一個方法會有大量的調用,而我們可以按照指定的條件,watch 到我們希望觀察的那一次調用。
說實話,我對 Arthas 也沒有什么研究,一開始還真不清楚原來 Arthas watch 可以按條件過濾,翻看一下官方文檔:https://arthas.aliyun.com/doc/watch#id6
條件表達式的例子
- $ watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
- Press Ctrl+C to abort.
- Affect(class-cnt:1 , method-cnt:1) cost in 68 ms.
- ts=2018-12-03 19:36:04; [cost=0.530255ms] result=@ArrayList[
- @Integer[-18178089],
- @MathGame[demo.MathGame@41cf53f9],
- ]
喏,這不是這么明顯的例子嗎?但是上面的例子,貌似只給出了一些簡單的訊息
- 可以直接用 watch 命令的參數(shù)項中增加條件表達式進行過濾
- 可以進行數(shù)值類型的過濾
但是,這個簡單的示例,并沒有解答我內心其他的疑惑:
- 我可以進行字符串、集合等復雜類型的判斷嗎?
- 這個表達式是 el、ognl 或者其他類型的表達式嗎?
帶著這些疑問,我記錄下了這篇文章,給不了解 Arthas watch 條件表達式的讀者們一些參考。
一些條件表達式的示例
有一些讀者可能僅僅是想知道“我該怎么實現(xiàn)使用 Arthas 條件 Watch”,為此,我在本文的第二節(jié)先介紹下我平時積累的一些實踐命令。
示例方法
- public void methodForWatch(int id, User user) {
- }
User 結構
- @Data
- public class User {
- private String name;
- private int age;
- private List<String> hobbies;
- }
另外準備一些請求,我會在每個示例中執(zhí)行相同的調用。示例請求:
- 1, new User("hanmeimei", 16, Arrays.asList("pubg", "lol"));
- 2, new User("liming", 17, Collections.singletonList("pubg"));
- 3, new User("tom", 18, Collections.singletonList("running"));
- 4, new User("jacky", 19, Collections.singletonList("food"));
示例 1:過濾 int 類型;過濾 id > 0 的請求
這其實就是官方的示例,我拿過來再貼一遍
- watch moe.cnkirito.arthas.WatchDemo methodForWatch "{params,returnObj}" "params[0]>0" -x 2
示例 2:過濾對象中的字符串類型;過濾 User 中 name = haimeimei 的請求
- watch moe.cnkirito.arthas.WatchDemo methodForWatch "{params,returnObj}" "params[1].getName().equals('liming')" -x 2
這里有三個注意點
- 使用 params[1] 這種數(shù)組訪問的方式,對應到 methodForWatch 方法的第二個參數(shù) User user
- 使用 getName() 這種方法調用的方式拿到 name 字段,并且使用 String 的 equals 方法進行字符串比對
- 由于 condition 表達式整體使用了雙引號 "",在 hanmeimei 該字面量上需要使用單引號 ''
示例 3:過濾集合中的元素;過濾對 pubg 感興趣的 User 相關的請求
- watch moe.cnkirito.arthas.WatchDemo methodForWatch {params,returnObj} "params[1].getHobbies().contains('pubg')" -x 2
示例 4:多個條件表達式
增加請求示例
- 5, new User("kirito", 20, null);
按照示例 3 的 watch 語句執(zhí)行,會發(fā)現(xiàn) Arthas 直接拋了一個空指針:
- watch failed, condition is: params[1].getHobbies().contains('pubg'), express is: {params,returnObj}, java.lang.NullPointerException: target is null for method contains, visit /Users/xujingfeng/logs/arthas/arthas.log for more details.
需要增加空指針的判斷:
- watch moe.cnkirito.actuator.demo.HelloController methodForWatch {params,returnObj} "params[1].getHobbies() != null && params[1].getHobbies().contains('pubg')" -x 2
吶,很簡單,可以直接使用 && 增加判斷條件。
ognl 實現(xiàn)條件過濾
可能有人要說了,Kirito 啊!你的公眾號最近是不是廣告發(fā)的太多了,深感愧疚,寫了一篇沒啥深度的原創(chuàng)文章來充數(shù)啊!那我當然要反駁啦,其實我看 Arthas 文檔的時候也是踩了坑的,索性我將這個過程也分享一下。
可能大家看了上面的示例會覺得這個 condition 表達式不就是跟 Java 里面的表達式差不多嗎?但其實我作為一個不太了解 Arthas 的弱雞,上面的用法純粹是我摸索出來的,在最開始的時候,參考 github 中的 issue,我使用的其實是其他的方式來實現(xiàn)的條件查詢,參考 issue:https://github.com/alibaba/arthas/issues/71。
看下 github 中的 Arthas 開源作者提供的按條件過濾的示例,可以發(fā)現(xiàn)跟上文中我介紹的過濾方式好像,有那么一點點的不同。注意上文的示例
- $ watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0"
watch 后的參數(shù)是由 4 部分組成的,分別是類名表達式,方法名表達式,觀察表達式,條件表達式。
而 issue 中給出的表達式
- $ watch com.taobao.container.Test test "params[0].{? #this.name == null }" -x 2
沒有第四部分:條件表達式。過濾條件被放到了觀察表達式的對象后,并且不是 Java 里面的表達式,而是 ognl 表達式。
- ognl 表達式官方參考文檔:https://commons.apache.org/proper/commons-ognl/language-guide.html
例如使用 ognl 表達式實現(xiàn)上面的示例 2,需要這么寫
示例 5:使用 ognl 表達式過濾對象中的字符串類型;過濾 User 中 name = haimeimei 的請求
- watch moe.cnkirito.actuator.demo.HelloController methodForWatch "params[1].{? #this.name == 'hanmeimei'}" -x 2
示例 2 和示例 5 的對比
聊到這里,如果你對 Arthas 比較熟悉,應該已經(jīng)意識到示例 5 ognl 過濾和示例 2 直接使用條件過濾表達式的區(qū)別了。ognl 這種過濾的方式,是針對對象的屬性的過濾,無論是否匹配,都會被算進 watch 的匹配次數(shù)中,只不過沒有匹配到的對象沒有輸出;而示例 2 中直接使用條件過濾表達式這種方式,更匹配我文首提出的需求,只有被條件表示式命中的請求,才會被算進 watch 次數(shù)中。你可以使用 -n 1 來限定 watch 匹配次數(shù),直觀地觀察到這兩個匹配方式的差異。
總結
本文簡單介紹了使用 Arthas 條件表達式使用中可能踩到的一些坑,示例 1~4 可以參考,用于過濾一些指定的請求,讓線上問題的定位變得更加高效。