在使用Arthas的過程中,感覺有些功能很好用,但是命令行的用起來會比較麻煩,,本著學習的態度,自己設計了一套無需停機,埋點,熱修復,監控等功能的應用,并提供了WEB頁面進行操作,并且會不斷集成一些常用的功能 .
1. 已經支持的操作
- 監控JVM、GC、啟動參數、線程、系統參數等大盤信息
- 對于正在運行中的程序,動態給某個方法織入代碼,獲取某個函數的執行時間
- 對于正在運行中的程序,動態給某個方法織入代碼, 獲取某個controller方法請求參數和返回值
- 對于正在運行中的程序,獲取全部線程占有CPU時間百分比,并支持查看指定線程運行的堆棧信息
- 熱修復,通過反編譯導出JAVA文件,修改后再導入通過classload重載入實現熱編譯(適用于空指針等bug,少量修改代碼就可解決問題時使用)
- 由于使用web頁面操作,ip需要改為目標進程機器ip(默認是localhost如果是本地運行無需修改)
關鍵技術
技術名稱 |
作用 |
Javaagent |
在運行前加載,或在運行中對字節碼進行操作 |
Virtualmachine |
在運行中附著到目標進程上,并調用agent |
架構部分
工程 |
技術 |
web |
Vue + Ant design |
server |
Springboot+Websocket(以后可能棄用Springboot改為NettyServer) |
agent |
netty + Javassist + cfr + JavaCompiler |
2. 如何安裝使用?
- 去阿里云Maven倉庫下載Jar包即可
(maven默認國外倉庫)
(阿里云Maven倉庫) 推薦使用!
Github源碼
搜索nobugboy或ETrace即可
獲取到下載地址,他這個鏈接會動態刷新,所以需要自己去搜索下再下載。
- 獲取到連接后
1. win直接點擊連接下載,mac/linux wget連接
2. 將文件改名為 ETrace-1.2.4.jar (點連接下載不用改)
2.運行
linux/macos 具體由你配置的環境變量名稱作為值,一般為$JAVA_HOME
windows $JAVA_HOME直接替換為javahome的絕對目錄即可
java -jar -Xbootclasspath/a:$JAVA_HOME/lib/tools.jar ETrace-1.2.4.jar
3. gif演示操作
- 演示大盤和線程追蹤,點擊線程直接可以獲取當前堆棧信息(定位高CPU原因)
2.演示織入打印執行時間(為了好看我睡眠了1s)
3.演示織入打印參數返回值
4. 熱修復,這個功能錄制太繁瑣,直接圖文吧
場景:一個同學提交了空指針異常的代碼到了生產環境,測試沒有覆蓋到。
@GetMApping(value = "/get/{id}",name = "根據id獲取用戶")
public User test(@PathVariable(value = "id",name = "id") String id){
User user = null;
user.setName("測試");
return user;
}
#調用一下試試
curl localhost:8080/get/1
出錯了!
exception is java.lang.NullPointerException
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-5.3.6.jar:5.3.6]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.6.jar:5.3.6]
此時服務已經在運行狀態,在不能重啟服務的情況下我們來修復,先將需要修改的類路徑找到,輸出到桌面(輸出路徑一定要在最后加 /)
修改空指針問題,編輯導出的文件
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.example.springboottestdemo.controller.User
*/
package com.example.springboottestdemo.controller;
import com.example.springboottestdemo.controller.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController(value="u7528u6237u76f8u5173u63a5u53e3")
public class TestController {
@GetMapping(value={"/last"}, name="getu8bf7u6c42u6d4bu8bd5u53c2u6570u63cfu8ff0")
public User test2(User user) {
return user;
}
@PostMapping(value={"/post"}, name="postu8bf7u6c42u6d4bu8bd5u53c2u6570u63cfu8ff0")
public User test1(@RequestBody User user) {
System.out.println((Object)user);
return user;
}
@GetMapping(value={"/get/{id}"}, name="u6839u636eidu83b7u53d6u7528u6237")
public User test(@PathVariable(value="id", name="id") String id) {
//將此處修復
User user = new User();
user.setName("u6d4bu8bd5");
return user;
}
}
看到控制臺 "compiler ok"字樣代表編譯完成,接下來重新load進去
看到控制臺 "redefine successful !"字樣代表load成功,此時我們再次請求剛才報錯的接口,錯誤消失代表成功修復。
curl localhost:8080/get/1
{"id":null,"name":"測試","us":null,"type":null}%
原文鏈接:
https://juejin.cn/post/6976178524608544775