Arthas | 熱更新線上代碼

前言

本文是我介紹 Arthas 系列文章的第一篇。java

通常線上問題比開發環境的問題更難解決,一個主要的緣由便在於開發態能夠任意 debug 斷點調試,而線上環境通常不容許遠程調試,因此在實踐中,我通常習慣用 Arthas 來定位線上的問題。安全

Arthas 是阿里巴巴開源的 Java 應用診斷利器服務器

Arthas 能夠完成不少騷操做,今天給你們介紹的 Arthas 診斷技巧即是 -- 熱更新線上代碼。在生產環境熱更新代碼,並非很好的行爲,可能會引起一些問題微信

  • 黑屏化的操做可能會致使誤操做併發

  • 不符合安全生產的規範,不知足可監控、可回滾、可降級編輯器

但有時候也有一些場景能夠考慮使用 Arthas 來熱更,例如開發環境沒法復現的問題、找到修復思路後臨時驗證等。函數

本文以 Arthas 3.1.7 版本爲例,主要使用到 jad/mc/redefine 三個指令。spa

示例

在 arthas-demo 示例中,一共有兩個類,一個 HelloService 類,sayHello 方法負責不斷的打印 hello world.net

public class HelloService {

public void sayHello() {
System.out.println("hello world");
}

}

HelloService 用於模擬咱們平常開發的一些業務 Service,另外還有一個 Main 函數,負責啓動進程,並循環調用debug

public class Main {

public static void main(String[] args) throws InterruptedException {
HelloService helloService = new HelloService();
while (true) {
Thread.sleep(1000);
helloService.sayHello();
}
}

}

需求

假設這段代碼運行在線上,咱們但願經過 Arthas 將 hello world 的輸出更改成 hello arthas

Arthas 修改熱更的邏輯主要分爲三步:

  • jad 命令反編譯出內存中的字節碼,生成 class 文件

  • 修改代碼,使用 mc 命令內存編譯新的 class 文件

  • redefine 從新加載新的 class 文件

從而達到熱更新的效果

jad 反編譯

當掛載上 Arthas 以後,執行

$ jad --source-only moe.cnkirito.arthas.demo.HelloService > /tmp/HelloService.java

將字節碼文件輸出到指定的位置,查看其內容,與示例中的源碼內容一致:

/*
* Decompiled with CFR.
*/

package moe.cnkirito.arthas.demo;

import java.io.PrintStream;

public class HelloService {
public void sayHello() {
System.out.println("hello world");
}
}

命令中 --source-only 的含義爲,只輸出源碼部分,若是不加這個參數,在反編譯出的內容頭部會攜帶類加載器的信息:

ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@20d5ad12

Location:
/Users/xujingfeng/IdeaProjects/arthas-demo/target/classes/

在服務器上能夠直接使用 vi 等編輯器對源碼進行編輯。將 hello world 改成 hello arthas,爲下一步作準備。

sc 查找類加載器

mc 命令編譯文件須要傳入該類對應類加載器的 hash 值,須要先使用 sc 命令查看 HelloService 的累加器信息

$ sc -d moe.cnkirito.arthas.demo.HelloService

輸出:

class-info moe.cnkirito.arthas.demo.HelloService
code-source /Users/xujingfeng/IdeaProjects/arthas-demo/target/classes/
name moe.cnkirito.arthas.demo.HelloService
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name HelloService
modifier public
annotation
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@20d5ad12
classLoaderHash 18b4aac2

最後一行 classLoaderHash 即爲 HelloService 的類加載器 hash 值。

Arthas 支持 grep,你也能夠簡化該操做爲:

sc -d moe.cnkirito.arthas.demo.HelloService | grep classLoaderHash

mc 內存編譯

$ mc -c 18b4aac2 /tmp/HelloService.java -d /tmp
Memory compiler output:
/tmp/moe/cnkirito/arthas/demo/HelloService.class

使用 -c 指定類加載器的 hash 值。編譯完成後,/tmp 目錄下會生成對應的 class 字節碼文件

redefine 熱更新代碼

$ redefine /tmp/moe/cnkirito/arthas/demo/HelloService.class

檢查結果

hello world
hello world
hello world
hello world
hello arthas
hello arthas
hello arthas
hello arthas

熱更新成功

常見問題

redefine 使用限制

  • 不容許新增或者刪除 field/method

    會出現相似下面的提示

    redefine error! java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
  • 運行中的方法不會馬上生效,會在下一次進入該方法時才能生效。

    很好理解,併發問題

mc 常見問題

  • mc 命令有可能失敗

    由於運行時環境和編譯時環境的 JDK 可能有版本差別,mc 可能會失敗。若是編譯失敗能夠在本地編譯好 .class 文件,再上傳到服務器

  • 當存在內部類時,一次會生成多個 class 文件

    public class HelloService {
    public void sayHello() {
    Inner.test();
    }

    public static class Inner {
    public static void test() {
    System.out.println("hello inner");
    }
    }

    }

    執行 mc

    $ mc -c 18b4aac2 /tmp/HelloService.java -d /tmp
    Memory compiler output:
    /tmp/moe/cnkirito/arthas/demo/HelloService$Inner.class
    /tmp/moe/cnkirito/arthas/demo/HelloService.class

    注意 redefine 時也能夠同時傳入多個入參

    $ redefine /tmp/moe/cnkirito/arthas/demo/HelloService$Inner.class /tmp/moe/cnkirito/arthas/demo/HelloService.class
    redefine success, size: 2

參考文章

https://blog.csdn.net/hengyunabc/article/details/87718469

Arthas 交流釘釘羣

羣號:21965291


本文分享自微信公衆號 - Kirito的技術分享(cnkirito)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索