阿里arthas分享

tpts發表於2020-12-04

arthas是阿里開源的Java診斷工具,可以對執行時的java程式進行分析,動態代理執行時的Java程式。artahs支援JDK6+,同時提供豐富的tab自動補全功能,進一步方便進行問題的定位和診斷。

  1. 這個類從哪個 jar 包載入的?為什麼會報各種類相關的 Exception?
  2. 我改的程式碼為什麼沒有執行到?難道是我沒 commit?分支搞錯了?
  3. 遇到問題無法線上上 debug,難道只能通過加日誌再重新發布嗎?
  4. 線上遇到某個使用者的資料處理有問題,但線上同樣無法 debug,線下無法重現!
  5. 是否有一個全域性視角來檢視系統的執行狀況?
  6. 有什麼辦法可以監控到JVM的實時執行狀態?

arthas基本介紹

        arthas是一個jar包,官方提供了幾種方式安裝,通常只需要下載jar包,然後java -jar arthas-boot.jar執行該jar包即可。

wget https://alibaba.github.io/arthas/arthas-boot.jar

java -jar arthas-boot.jar

基本命令介紹:

dashboard/thread/jvm/sysprop/sysenv

sc/sm

jad/classloader/mc/redefine

monitor/watch/trace/stack

表示式核心變數:

public class Advice {

 

    private final ClassLoader loader;

    private final Class<?> clazz;

    private final ArthasMethod method;

    private final Object target;

    private final Object[] params;

    private final Object returnObj;

    private final Throwable throwExp;

    private final boolean isBefore;

    private final boolean isThrow;

    private final boolean isReturn;

    // getter/setter 

}  

 

arthas原理

        java instrumentation,該機制的最大作用就是類定義動態改變和操作。在java se 5及後續版本中,可以通過java -javaagent引數指定一個特定的jar檔案(包含Instrumentation代理)來啟動Instrumentation的代理程式。

        se5:編寫一個java類,包含如下兩個方法中的一個:

public static void premain(String agentArgs, Instrumentation inst);  [1]

public static void premain(String agentArgs); [2]

在這個 premain 函式中,開發者可以進行對類的各種操作。

agentArgs 是 premain 函式得到的程式引數,隨同 “–javaagent”一起傳入。與 main 函式不同的是,這個引數是一個字串而不是一個字串陣列,如果程式引數有多個,程式將自行解析這個字串。

inst 是一個 java.lang.instrument.Instrumentation 的例項,由 JVM 自動傳入。java.lang.instrument.Instrumentation 是 instrument 包中定義的一個介面,也是這個包的核心部分,集中了其中幾乎所有的功能方法,例如類定義的轉換和操作等等。在se5中,所做的instrumentation僅限於main函式執行前,這樣的方式存在一定的侷限性。

      se6:虛擬機器啟動之後的instrument,比se5中又多了兩個方法:

public static void agentmain (String agentArgs, Instrumentation inst);          [1]

public static void agentmain (String agentArgs);            [2]

在se6中,開發者可以再main函式開始執行以後,在啟動自己的instrumentation程式。

基於Attach API實現,是sun公司提供的一套擴充套件的API,用來向目標JVM附著(Attach)代理工具程式的。提供一種jvm程式間通訊的能力,能讓一個程式傳命令給另外一個程式,可以理解為一種虛擬機器級別的aop實現。

Attach API 有 2 個主要的類,都在 com.sun.tools.attach 包裡面: VirtualMachine 代表一個 Java 虛擬機器,也就是程式需要監控的目標虛擬機器,提供了 JVM 列舉,Attach 動作和 Detach 動作(Attach 動作的相反行為,從 JVM 上面解除一個代理)等等 ; VirtualMachineDescriptor 則是一個描述虛擬機器的容器類,配合 VirtualMachine 類完成各種功能。

import java.lang.instrument.ClassDefinition;

import java.lang.instrument.Instrumentation;

import java.lang.instrument.UnmodifiableClassException;

  

public class AgentMain {

   public static void agentmain(String agentArgs, Instrumentation inst)

           throws ClassNotFoundException, UnmodifiableClassException,

           InterruptedException {

       inst.addTransformer(new Transformer (), true);

       inst.retransformClasses(TransClass.class);

       System.out.println("Agent Main Done");

   }

}

 

Transformer類:

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.lang.instrument.ClassFileTransformer;

import java.lang.instrument.IllegalClassFormatException;

import java.security.ProtectionDomain;

  

class Transformer implements ClassFileTransformer {

  

   public static final String classNumberReturns2 = "TransClass.class.2";

  

   public static byte[] getBytesFromFile(String fileName) {

       try {

           // precondition

           File file = new File(fileName);

           InputStream is = new FileInputStream(file);

           long length = file.length();

           byte[] bytes = new byte[(int) length];

  

           // Read in the bytes

           int offset = 0;

           int numRead = 0;

           while (offset <bytes.length

                   && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {

               offset += numRead;

           }

  

           if (offset < bytes.length) {

               throw new IOException("Could not completely read file "

                       + file.getName());

           }

           is.close();

           return bytes;

       catch (Exception e) {

           System.out.println("error occurs in _ClassTransformer!"

                   + e.getClass().getName());

           return null;

       }

   }

  

   public byte[] transform(ClassLoader l, String className, Class<?> c,

           ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {

       if (!className.equals("TransClass")) {

           return null;

       }

       return getBytesFromFile(classNumberReturns2);

  

   }

}


除此之外,se6還提供了本地方法的Instrumentation和 BootClassPath/SystemClassPath的動態增補。

arthas與現有系統整合

啟動arthas程式之後,是開放了http埠的,可以直接訪問http://localhost:8563

可以把arthas web server部署到一臺有超級許可權訪問其他伺服器的機器上。然後使用者選了某臺機器的ip之後,通過ssh去對應的機器上啟動arthas,並獲取http埠,通過nginx或java程式碼轉發,然後就可以有統一的介面操作所有的機器。

虛機

1、虛機上下載arthas-boot包

2、登入到對應伺服器,基於應用名查詢相應的pid

3、如果該埠沒有被監聽,直接attach該pid之後返回

QAE-jar包

目前沒有想到比較好的方法,可以統一整合到現有的系統當中,目前只能是使用的時候從網路上下載,需要開啟外網功能,然後啟動jar包。

QAE-映象

1、直接把arthas-boot.jar包copy到基礎映象中,放到/usr/local目錄下

2、在qae新增8563埠,然後啟動arthas-boot,jar包,從環境變數中獲取PORT_8563真實的對映介面,直接跳轉到對應頁面就可以了

 

 

 

 

 

參考文件

1、Java.lang.instrument使用 https://www.cnblogs.com/wade-luffy/p/6078301.html

2、記錄如何使用arthas進行遠端訪問 https://github.com/alibaba/arthas/issues/442

3、web console功能支援 https://github.com/alibaba/arthas/issues/15

4、arthas 快速入門 https://alibaba.github.io/arthas/quick-start.html

5、Arthas實踐--使用redefine排查應用奇怪的日誌來源 https://yq.aliyun.com/articles/657215?utm_content=m_1000019894

相關文章