Arthas 進階教程

Arthas 進階教程

啟動math-game

下載demo-arthas-spring-boot.jar,再用java -jar命令啟動:

wget //github.com/hengyunabc/spring-boot-inside/raw/master/demo-arthas-spring-boot/demo-arthas-spring-boot.jar java -jar demo-arthas-spring-boot.jar

demo-arthas-spring-boot是一個很簡單的spring boot應用,源程式碼:查看

啟動之後,可以訪問80埠: //2886795286-80-host11nc.environments.katacoda.com

Demo Web

啟動arthas-boot

在新的Terminal 2里,下載arthas-boot.jar,再用java -jar命令啟動:

wget //arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar --target-ip 0.0.0.0

arthas-bootArthas的啟動程式,它啟動後,會列出所有的Java進程,用戶可以選擇需要診斷的目標進程。

選擇第一個進程,輸入 1 ,再Enter/回車

Attach成功之後,會列印Arthas LOGO。輸入 help 可以獲取到更多的幫助資訊。

查看JVM資訊

下面介紹Arthas里查看JVM資訊的命令。

sysprop

sysprop 可以列印所有的System Properties資訊。

也可以指定單個key: sysprop java.version

也可以通過grep來過濾: sysprop | grep user

可以設置新的value: sysprop testKey testValue

sysenv

sysenv 命令可以獲取到環境變數。和sysprop命令類似。

jvm

jvm 命令會列印出JVM的各種詳細資訊。

dashboard

dashboard 命令可以查看當前系統的實時數據面板。

輸入 Q 或者 Ctrl+C 可以退出dashboard命令。

Tips

為了更好使用Arthas,下面先介紹Arthas里的一些使用技巧。

help

Arthas里每一個命令都有詳細的幫助資訊。可以用-h來查看。幫助資訊里有EXAMPLESWIKI鏈接。

比如:

sysprop -h

自動補全

Arthas支援豐富的自動補全功能,在使用有疑惑時,可以輸入Tab來獲取更多資訊。

比如輸入 sysprop java. 之後,再輸入Tab,會補全出對應的key:

$ sysprop java.

sc/sm 查看已載入的類

下面介紹Arthas里查找已載入類的命令。

sc

sc 命令可以查找到所有JVM已經載入到的類。

如果搜索的是介面,還會搜索所有的實現類。比如查看所有的Filter實現類:

sc javax.servlet.Filter

通過-d參數,可以列印出類載入的具體資訊,很方便查找類載入問題。

sc -d javax.servlet.Filter

sc支援通配,比如搜索所有的StringUtils

sc *StringUtils

sm

sm命令則是查找類的具體函數。比如:

sm java.math.RoundingMode

通過-d參數可以列印函數的具體屬性:

sm -d java.math.RoundingMode

也可以查找特定的函數,比如查找構造函數:

sm java.math.RoundingMode <init>

Jad

可以通過 jad 命令來反編譯程式碼:

jad com.example.demo.arthas.user.UserController

通過--source-only參數可以只列印出在反編譯的源程式碼:

jad --source-only com.example.demo.arthas.user.UserController

Ognl -> 重要

在Arthas里,有一個單獨的ognl命令,可以動態執行程式碼。

調用static函數

ognl '@[email protected]("hello ognl")'

可以檢查Terminal 1(不是arthas的Terminal 2)里的進程輸出,可以發現列印出了hello ognl

查找UserController的ClassLoader

sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash classLoaderHash   1be6f5c3

注意hashcode是變化的,需要先查看當前的ClassLoader資訊,提取對應ClassLoader的hashcode。

如果你使用-c,你需要手動輸入hashcode:-c <hashcode>

$ ognl -c 1be6f5c3 @com.example.demo.arthas.user.UserController@logger

對於只有唯一實例的ClassLoader可以通過--classLoaderClass指定class name,使用起來更加方便:

$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader  @org.springframework.boot.SpringApplication@logger@Slf4jLocationAwareLog[    FQCN=@String[org.apache.commons.logging.LogAdapter$Slf4jLocationAwareLog],    name=@String[org.springframework.boot.SpringApplication],    logger=@Logger[Logger[org.springframework.boot.SpringApplication]],]

--classLoaderClass 的值是ClassLoader的類名,只有匹配到唯一的ClassLoader實例時才能工作,目的是方便輸入通用命令,而-c <hashcode>是動態變化的。

獲取靜態類的靜態欄位

獲取UserController類里的logger欄位:

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader @com.example.demo.arthas.user.UserController@logger

還可以通過-x參數控制返回值的展開層數。比如:

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -x 2 @com.example.demo.arthas.user.UserController@logger

執行多行表達式,賦值給臨時變數,返回一個List

ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'@ArrayList[    @String[/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/jre],    @String[Java(TM) SE Runtime Environment],]

更多

在Arthas里ognl表達式是很重要的功能,在很多命令里都可以使用ognl表達式。

一些更複雜的用法,可以參考:

案例: 排查函數調用異常

現象

目前,訪問 //localhost/user/0 ,會返回500異常:

curl //localhost/user/0{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}

但請求的具體參數,異常棧是什麼呢?

查看UserController的 參數/異常

在Arthas里執行:

watch com.example.demo.arthas.user.UserController * '{params, throwExp}'
  1. 第一個參數是類名,支援通配
  2. 第二個參數是函數名,支援通配

訪問 curl //localhost/user/0 ,watch命令會列印調用的參數和異常

$ watch com.example.demo.arthas.user.UserController * '{params, throwExp}'Press Q or Ctrl+C to abort.Affect(class-cnt:1 , method-cnt:2) cost in 53 ms.ts=2019-02-15 01:35:25; [cost=0.996655ms] result=@ArrayList[    @Object[][isEmpty=false;size=1],    @IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],]

可以看到實際拋出的異常是IllegalArgumentException

可以輸入 Q 或者 Ctrl+C 退出watch命令。

如果想把獲取到的結果展開,可以用-x參數:

watch com.example.demo.arthas.user.UserController * '{params, throwExp}' -x 2

返回值表達式

在上面的例子里,第三個參數是返回值表達式,它實際上是一個ognl表達式,它支援一些內置對象:

  • loader
  • clazz
  • method
  • target
  • params
  • returnObj
  • throwExp
  • isBefore
  • isThrow
  • isReturn

你可以利用這些內置對象來組成不同的表達式。比如返回一個數組:

watch com.example.demo.arthas.user.UserController * '{params[0], target, returnObj}'

更多參考: //arthas.aliyun.com/doc/advice-class.html

條件表達式

watch命令支援在第4個參數里寫條件表達式,比如:

watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'

當訪問 //2886795301-80-host12nc.environments.katacoda.com/user/1 時,watch命令沒有輸出

當訪問 //2886795301-80-host12nc.environments.katacoda.com/user/101 時,watch會列印出結果。

$ watch com.example.demo.arthas.user.UserController * returnObj 'params[0] > 100'Press Q or Ctrl+C to abort.Affect(class-cnt:1 , method-cnt:2) cost in 47 ms.ts=2019-02-13 19:42:12; [cost=0.821443ms] result=@User[    id=@Integer[101],    name=@String[name101],]

當異常時捕獲

watch命令支援-e選項,表示只捕獲拋出異常時的請求:

watch com.example.demo.arthas.user.UserController * "{params[0],throwExp}" -e

按照耗時進行過濾

watch命令支援按請求耗時進行過濾,比如:

watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'

案例: 熱更新程式碼

下面介紹通過jad/mc/redefine 命令實現動態更新程式碼的功能。

目前,訪問 //localhost/user/0 ,會返回500異常:

curl //localhost/user/0{"timestamp":1550223186170,"status":500,"error":"Internal Server Error","exception":"java.lang.IllegalArgumentException","message":"id < 1","path":"/user/0"}

下面通過熱更新程式碼,修改這個邏輯。

jad反編譯UserController

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java

jad反編譯的結果保存在 /tmp/UserController.java文件里了。

再打開一個Terminal 3,然後用vim來編輯/tmp/UserController.java

vim /tmp/UserController.java

比如當 user id 小於1時,也正常返回,不拋出異常:

    @GetMapping(value={"/user/{id}"})    public User findUserById(@PathVariable Integer id) {        logger.info("id: {}", (Object)id);        if (id != null && id < 1) {            return new User(id, "name" + id);            // throw new IllegalArgumentException("id < 1");        }        return new User(id.intValue(), "name" + id);    }

sc查找載入UserController的ClassLoader

sc -d *UserController | grep classLoaderHash$ sc -d *UserController | grep classLoaderHash classLoaderHash   1be6f5c3

可以發現是 spring boot LaunchedURLClassLoader@1be6f5c3 載入的。

請記下你的classLoaderHash,後面需要使用它。在這裡,它是 1be6f5c3

mc

保存好/tmp/UserController.java之後,使用mc(Memory Compiler)命令來編譯,並且通過-c或者--classLoaderClass參數指定ClassLoader:

mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmpMemory compiler output:/tmp/com/example/demo/arthas/user/UserController.classAffect(row-cnt:1) cost in 346 ms

也可以通過mc -c <classLoaderHash> /tmp/UserController.java -d /tmp,使用-c參數指定ClassLoaderHash:

$ mc -c 1be6f5c3 /tmp/UserController.java -d /tmp

redefine

再使用redefine命令重新載入新編譯好的UserController.class

redefine /tmp/com/example/demo/arthas/user/UserController.class$ redefine /tmp/com/example/demo/arthas/user/UserController.classredefine success, size: 1

熱修改程式碼結果

redefine成功之後,再次訪問 //2886795301-80-host12nc.environments.katacoda.com/user/0 ,結果是:

{  "id": 0,  "name": "name0"}

案例: 動態更新應用Logger Level

在這個案例里,動態修改應用的Logger Level。

查找UserController的ClassLoader

sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash classLoaderHash   1be6f5c3

用ognl獲取logger

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'@Logger[    serialVersionUID=@Long[5454405123156820674],    FQCN=@String[ch.qos.logback.classic.Logger],    name=@String[com.example.demo.arthas.user.UserController],    level=null,    effectiveLevelInt=@Integer[20000],    parent=@Logger[Logger[com.example.demo.arthas.user]],    childrenList=null,    aai=null,    additive=@Boolean[true],    loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]

可以知道UserController@logger實際使用的是logback。可以看到level=null,則說明實際最終的level是從root logger里來的。

單獨設置UserController的logger level

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@[email protected](@ch.qos.logback.classic.Level@DEBUG)'

再次獲取UserController@logger,可以發現已經是DEBUG了:

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'@Logger[    serialVersionUID=@Long[5454405123156820674],    FQCN=@String[ch.qos.logback.classic.Logger],    name=@String[com.example.demo.arthas.user.UserController],    level=@Level[DEBUG],    effectiveLevelInt=@Integer[10000],    parent=@Logger[Logger[com.example.demo.arthas.user]],    childrenList=null,    aai=null,    additive=@Boolean[true],    loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]

修改logback的全局logger level

通過獲取root logger,可以修改全局的logger level:

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'

案例: 排查logger衝突問題

在這個案例里,展示排查logger衝突的方法。

確認應用使用的logger系統

UserController為例,它使用的是slf4j api,但實際使用到的logger系統是logback。

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'$ ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '@com.example.demo.arthas.user.UserController@logger'@Logger[    serialVersionUID=@Long[5454405123156820674],    FQCN=@String[ch.qos.logback.classic.Logger],    name=@String[com.example.demo.arthas.user.UserController],    level=null,    effectiveLevelInt=@Integer[20000],    parent=@Logger[Logger[com.example.demo.arthas.user]],    childrenList=null,    aai=null,    additive=@Boolean[true],    loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]

獲取logback實際載入的配置文件

ognl --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader '#[email protected]@getLogger("root").loggerContext.objectMap, #map1.get("CONFIGURATION_WATCH_LIST")'

使用classloader命令查找可能存在的logger配置文件

classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml jar:file:/Users/hengyunabc/code/java/spring-boot-inside/demo-arthas-spring-boot/target/demo-arthas-spring-boot-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/logback-spring.xmlAffect(row-cnt:1) cost in 13 ms.

可以知道載入的配置的具體來源。

可以嘗試載入容易衝突的文件:

classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback.xmlclassloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r log4j.properties

案例: 獲取Spring Context

在這個案例里,展示獲取spring context,再獲取bean,然後調用函數。

使用tt命令獲取到spring context

tt即 TimeTunnel,它可以記錄下指定方法每次調用的入參和返回資訊,並能對這些不同的時間下調用進行觀測。

tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod

訪問://2886795309-80-host09nc.environments.katacoda.com/user/1

可以看到tt命令捕獲到了一個請求:

$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdaptePress Q or Ctrl+C to abort.Affect(class-cnt:1 , method-cnt:1) cost in 252 ms. INDE  TIMESTAMP    COST(  IS-R  IS-  OBJECT     CLASS               METHOD X                  ms)    ET    EXP----------------------------------------------------------------------------------------- 1000  2019-02-15   4.583  true  fal  0xc93cf1a  RequestMappingHand  invokeHandlerMethod       15:38:32     923          se              lerAdapter

使用tt命令從調用記錄里獲取到spring context

輸入 Q 或者 Ctrl + C 退出上面的 tt -t命令。

tt -i 1000 -w 'target.getApplicationContext()'$ tt -i 1000 -w 'target.getApplicationContext()'@AnnotationConfigEmbeddedWebApplicationContext[    reader=@AnnotatedBeanDefinitionReader[org.springframework.context.annotation.AnnotatedBeanDefinitionReader@2e457641],    scanner=@ClassPathBeanDefinitionScanner[org.springframework.context.annotation.ClassPathBeanDefinitionScanner@6eb38026],    annotatedClasses=null,    basePackages=null,]Affect(row-cnt:1) cost in 439 ms.

獲取spring bean,並調用函數

tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'

結果是:

$ tt -i 1000 -w 'target.getApplicationContext().getBean("helloWorldService").getHelloMessage()'@String[Hello World]Affect(row-cnt:1) cost in 52 ms.

案例: 排查HTTP請求返回401

在這個案例里,展示排查HTTP 401問題的技巧。

訪問: //2886795309-80-host09nc.environments.katacoda.com/admin

結果是:

Something went wrong: 401 Unauthorized

我們知道401通常是被許可權管理的Filter攔截了,那麼到底是哪個Filter處理了這個請求,返回了401?

跟蹤所有的Filter函數

開始trace:

trace javax.servlet.Filter *

訪問: //2886795309-80-host09nc.environments.katacoda.com/admin

可以在調用樹的最深層,找到AdminFilterConfig$AdminFilter返回了401

+---[3.806273ms] javax.servlet.FilterChain:doFilter()|   `---[3.447472ms] com.example.demo.arthas.AdminFilterConfig$AdminFilter:doFilter()|       `---[0.17259ms] javax.servlet.http.HttpServletResponse:sendError()

通過stack獲取調用棧

上面是通過trace命令來獲取資訊,從結果里,我們可以知道通過stack跟蹤HttpServletResponse:sendError(),同樣可以知道是哪個Filter返回了401

執行:

stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'

訪問: //2886795309-80-host09nc.environments.katacoda.com/admin

$ stack javax.servlet.http.HttpServletResponse sendError 'params[0]==401'Press Q or Ctrl+C to abort.Affect(class-cnt:2 , method-cnt:4) cost in 87 ms.ts=2019-02-15 16:44:06;thread_name=http-nio-8080-exec-6;id=16;is_daemon=true;priority=5;TCCL=org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedWebappClassLoader@8546cd5    @org.apache.catalina.connector.ResponseFacade.sendError()        at com.example.demo.arthas.AdminFilterConfig$AdminFilter.doFilter(AdminFilterConfig.java:38)        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)

案例: 排查HTTP請求返回404

在這個案例里,展示排查HTTP 404問題的技巧。

訪問: //2886795309-80-host09nc.environments.katacoda.com/a.txt

結果是:

Something went wrong: 404 Not Found

那麼到底是哪個Servlet處理了這個請求,返回了404?

跟蹤所有的Servlet函數

開始trace:

trace javax.servlet.Servlet * > /tmp/servlet.txt

訪問: //2886795309-80-host09nc.environments.katacoda.com/a.txt

Terminal 3里,查看/tmp/servlet.txt的內容:

less /tmp/servlet.txt

/tmp/servlet.txt里的內容會比較多,需要耐心找到調用樹里最長的地方。

可以發現請求最終是被freemarker處理的:

`---[13.974188ms] org.springframework.web.servlet.ViewResolver:resolveViewName()    +---[0.045561ms] javax.servlet.GenericServlet:<init>()    +---[min=0.045545ms,max=0.074342ms,total=0.119887ms,count=2] org.springframework.web.servlet.view.freemarker.FreeMarkerView$GenericServletAdapter:<init>()    +---[0.170895ms] javax.servlet.GenericServlet:init()    |   `---[0.068578ms] javax.servlet.GenericServlet:init()    |       `---[0.021793ms] javax.servlet.GenericServlet:init()    `---[0.164035ms] javax.servlet.GenericServlet:getServletContext()

案例: 理解Spring Boot應用的ClassLoader結構

下面介紹classloader命令的功能。

先訪問一個jsp網頁,觸發jsp的載入: //2886795309-80-host09nc.environments.katacoda.com/hello

列出所有ClassLoader

classloader -l$ classloader -l name                                                             loadedCount  hash      parent BootstrapClassLoader                                             2724         null      null com.taobao.arthas.agent.ArthasClassloader@411ce1ab               2009         411ce1ab  sun.misc.Launcher$ExtClassLoader@7494e528 com.taobao.arthas.agent.ArthasClassloader@22ae1234               1253         22ae1234  sun.misc.Launcher$ExtClassLoader@7494e528 org.apache.jasper.servlet.JasperLoader@65361d9a                  1            65361d9a  TomcatEmbeddedWebappClassLoader                                                                                           context: ROOT                                                                                           delegate: true                                                                                         ----------> Parent Classloader:                                                                                         org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3 TomcatEmbeddedWebappClassLoader                                  0            8546cd5   org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3   context: ROOT   delegate: true ----------> Parent Classloader: org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3 org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3  5416         1be6f5c3  sun.misc.Launcher$AppClassLoader@3d4eac69 sun.misc.Launcher$AppClassLoader@3d4eac69                        45           3d4eac69  sun.misc.Launcher$ExtClassLoader@7494e528 sun.misc.Launcher$ExtClassLoader@7494e528                        4            7494e528  null
  • TomcatEmbeddedWebappClassLoader 載入的class數量是0,所以在spring boot embedded tomcat里,它只是一個空殼,所有的類載入都是LaunchedURLClassLoader完成的

列出ClassLoader里載入的所有類

列出上面的org.apache.jasper.servlet.JasperLoader載入的類:

classloader -a --classLoaderClass org.apache.jasper.servlet.JasperLoader$ classloader -a --classLoaderClass apache.jasper.servlet.JasperLoader hash:1698045338, org.apache.jasper.servlet.JasperLoader@65361d9a org.apache.jsp.jsp.hello_jsp
  • 註:同ognl, 也可用-c <hashcode>而不用--classLoaderClass指定

反編譯jsp的程式碼

jad org.apache.jsp.jsp.hello_jsp$ jad org.apache.jsp.jsp.hello_jspClassLoader:+-org.apache.jasper.servlet.JasperLoader@65361d9a  +-TomcatEmbeddedWebappClassLoader      context: ROOT...

查看ClassLoader樹

classloader -t$ classloader -t+-BootstrapClassLoader+-sun.misc.Launcher$ExtClassLoader@28cbbddd  +-com.taobao.arthas.agent.ArthasClassloader@8c25e55  +-sun.misc.Launcher$AppClassLoader@55f96302    +-org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3      +-TomcatEmbeddedWebappClassLoader          context: ROOT          delegate: true        ----------> Parent Classloader:        org.springframework.boot.loader.LaunchedURLClassLoader@1be6f5c3        +-org.apache.jasper.servlet.JasperLoader@21ae0fe2

注意:請使用你的classLoaderHash值覆蓋 <classLoaderHash> ,然後手動執行下面相關命令:

列出ClassLoader的urls

比如上面查看到的spring LaunchedURLClassLoader的 hashcode是1be6f5c3,可以通過-c或者--classLoaderClass參數來列出它的所有urls:

classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoaderjar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/lib/spring-boot-starter-aop-1.5.13.RELEASE.jar!/...

載入指定ClassLoader里的資源文件

查找指定的資源文件: classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml

$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader -r logback-spring.xml jar:file:/home/scrapbook/tutorial/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/logback-spring.xml

嘗試載入指定的類

比如用上面的spring LaunchedURLClassLoader 嘗試載入 java.lang.String

classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --load java.lang.String$ classloader --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --load java.lang.Stringload class success. class-info        java.lang.String code-source name              java.lang.String isInterface       false isAnnotation      false isEnum            false isAnonymousClass  false isArray           false isLocalClass      false isMemberClass     false isPrimitive       false isSynthetic       false simple-name       String modifier          final,public annotation interfaces        java.io.Serializable,java.lang.Comparable,java.lang.CharSequence super-class       +-java.lang.Object class-loader classLoaderHash   null

案例:查找Top N執行緒

查看所有執行緒資訊

thread

查看具體執行緒的棧

查看執行緒ID 16的棧:

thread 16

查看CPU使用率top n執行緒的棧

thread -n 3

查看5秒內的CPU使用率top n執行緒棧

thread -n 3 -i 5000

查找執行緒是否有阻塞

thread -b

Web Console

Arthas支援通過Web Socket來連接。

教程里的Web Console

//2886795309-8563-host09nc.environments.katacoda.com/?ip=2886795309-8563-host09nc.environments.katacoda.com&port=80

注意:教程里訪問的是80埠,因為做了埠轉發。在本地體驗時,需要訪問8563埠。

本地體驗

當在本地啟動時,可以訪問 //127.0.0.1:8563/ ,通過瀏覽器來使用Arthas。

Arthas WebConsole

推薦通過「快速入門」來體驗: //arthas.aliyun.com/doc/quick-start.html

Exit/Stop

reset

Arthas在 watch/trace 等命令時,實際上是修改了應用的位元組碼,插入增強的程式碼。顯式執行 reset 命令,可以清除掉這些增強程式碼。

退出Arthas

exit 或者 quit 命令可以退出Arthas。

退出Arthas之後,還可以再次用 java -jar arthas-boot.jar 來連接。

徹底退出Arthas

exit/quit命令只是退出當前session,arthas server還在目標進程中運行。

想完全退出Arthas,可以執行 stop 命令。

Arthas-boot支援的參數

arthas-boot.jar 支援很多參數,可以執行 java -jar arthas-boot.jar -h 來查看。

允許外部訪問

默認情況下, arthas server偵聽的是 127.0.0.1 這個IP,如果希望遠程可以訪問,可以使用--target-ip的參數。

java -jar arthas-boot.jar --target-ip

列出所有的版本

java -jar arthas-boot.jar --versions

使用指定版本:

java -jar arthas-boot.jar --use-version 3.1.0

只偵聽Telnet埠,不偵聽HTTP埠

java -jar arthas-boot.jar --telnet-port 9999 --http-port -1

列印運行的詳情

java -jar arthas-boot.jar -v
Tags: