Idea進行java應用的遠端除錯Remote debugging

LanceYa 發表於 2021-10-14
Java

本文可以解決如下兩個問題:
1.如何處理和除錯那些只發生在生產環境(或其他遠端環境)而本地開發環境可能沒辦法重現的“問題”。
2.只有一個可以部署的war/jar包,只有class沒有java原始碼,而應用部署在本地/遠端後,如何去除錯
解決方案:部署遠端除錯
遠端除錯包括兩個步驟:

  1. 啟動Tomcat啟用遠端除錯
    
  2. 用 IDE (這裡用IntelliJ IDEA)要能夠除錯遠端Tomcat應用
    

Tomcat啟用遠端除錯
根據tomcat所執行的作業系統而有些微的不同。但是不管用哪種方法,這些配置的背後都做了同一件事:傳遞特定的啟動引數給 JVM,讓它啟用遠端除錯(remote debugging)。
JVM 啟用遠端除錯的啟動引數有 JPDA_OPTS, CATALINA_OPTS 和 JAVA_OPTS。其中 JAVA_OPTS 是通常不建議使用的, 因為基於 JAVA_OPTS 的引數設定會暴露給所有的 JVM 應用, 而 CATALINA_OPTS 定義的設定值限制在Tomcat 內。

  • 1 使用JPDA_OPTS

在 CATALINA_HOME/bin 目錄下建立可執行指令碼檔案 setenv.sh ( Windows 建立 setenv.bat ),加入內容:
Linux setenv.sh
export JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
Windows setenv.bat
set JPDA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
這些引數要做的事情就是啟用遠端除錯和配置有效的選項:

  1. 指定執行的被除錯應用和除錯者之間的通訊協議,(transport=dt_socket)
    
  2. 遠端被除錯應用開通的埠,(address=1043), 可定義其他埠,比如9999
    
  3. server=y 表示這個 JVM 即將被除錯
    
  4. suspend=n 用來告知 JVM 立即執行,不要等待未來將要附著上/連上(attached)的除錯者。如果設成 y, 則應用將暫停不執行,直到有除錯者連線上
    
   suspend=y的一個比較適用的場景是,當debug一個會阻止應用成功啟動的問題時, 通過suspend=y可以確保除錯者連上來之後再啟動應用,否則應用已經啟動報錯了再除錯也沒意義了。

當然上面的設定也可以直接放到 catalina.sh (catalina.bat )內,但是有個 setevn.* 額外的配置檔案一直是最佳選擇, tomcat會自動讀取。
還有一種比較老的配置方法來啟用遠端除錯:
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=1043,suspend=n
-Xdebug and -Xrunjdw 與我們上面推薦的設定不同之處在於, 它是一種舊方式,適用於JVM 小於 JAVA 5.0 的版本(包括5.0), 而 agentlib:jdwp適用於 JAVA 5.0 和以後版本。
最後通過下面的命令列啟動tomcat,即可完成tomcat啟用遠端除錯啦。
$CATALINA_HOME/bin/catalina.sh jpda start

  • 2 使用 JAVA_OPTS / CATALINA_OPTS

如果你是在 Windows 系統把 Tomcat 作為系統服務來執行的,直接開啟 Apache Tomcat 的屬性對話方塊,在Java Tab也新增啟動引數:

    -agentlib:jdwp=transport=dt_socket,
    address=1043,server=y,suspend=n
    請確保每一條配置都是新的行,引數選項之間沒有空格

但如果Tomcat沒有作為 Windows 系統服務, 啟用方法與前面類似,在 setenv.bat 檔案中寫入:
set CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
如果執行在Linux上, setenv.sh 中寫入:
export CATALINA_OPTS="-agentlib:jdwp=transport=dt_socket,address=1043,server=y,suspend=n"
按照普通的方式啟動Tomcat即可;

    ./startup.sh
    或者
    ./catalina.sh start
  • 3 使用JPDA啟動

最後一種啟用遠端除錯的方式是用 JPDA 切換, 用如下的啟動命令將使用預設值自動啟用遠端除錯,
catalina jpda start
該命令預設使用的設定是
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n
如果你想要修改預設設定中的選項怎麼辦?可以通過修改 Tomcat 需要的這些環境變數來實現:

    //JPDA_TRANSPORT: 指定 jpda 傳輸協議
    //JPDA_ADDRESS: 指定遠端除錯埠
    //JPDA_SUSPEND: 指定 jvm 啟動暫緩
  
    export JDPA_ADDRESS=”8080“

然後再執行 catalina jpda start , 那麼遠端除錯的埠將變成8080
配置Intellj Idea
確定遠端 Tomcat 啟動的應用已經開啟了遠端除錯, 下一件事情就是配置Intellij Idea了。這裡仍然有兩種方式:Remote Tomcat 或者 Remote。
1 使用 Remote Tomcat 配置
首先保證 IDEA 裡面已經開啟了需要遠端除錯的工程原始碼,
然後點選 Run ➝ Edit Configurations ➝ + 按鈕 ➝ Tomcat Server ➝ Remote

輸入必要的遠端 IP 地址和埠(Tomcat http埠);

然後轉到 Startup / Connnection Tab 頁,選擇 ”Debug", 輸入遠端除錯埠。

儲存後,開始 debug 啟動遠端除錯,如果執行成功會顯示如下的介面,然後在原始碼加斷點開始除錯。

2 使用 Remote 配置(推薦)
第一個方法有個缺陷,你開啟的工程原始碼必須是編譯通過的工程,否則會啟動會報錯;
第二種方法可以在你的工程目錄,不是一個完整的可以部署的工程,甚至是一個解壓縮的 war/ jar 的情況下都可以除錯。
同上步驟,只是選擇“Remote",然後輸入Name, 修改Host, Port 即可, 儲存後開始Debug。

二。如果只有一個可部署的war包,沒有原始碼,在遠端已經部署完畢。這時我想除錯那個遠端應用,怎麼做呢?
解壓縮war包到一個資料夾,然後用Intellij Idea開啟這個資料夾,編譯的Class都在 WEB-INF/classes 目錄下
找到要debug的那個class, 通過Idea反編譯出來的類程式碼,拷貝到一個新的檔案Name.java
雖然可以看到各種的編譯錯誤,但是完全不影響你啟動,程式碼中加斷點和除錯。
遠端JVM除錯怎麼工作的
一切源於被稱作 Agents 的東西。
執行著各種編譯過的 .class 檔案的JVM, 有一種特性,可以允許外部的庫(Java或C++寫的libraries)在執行時注入到 JVM 中。這些外部的庫就稱作 Agents, 他們有能力修改執行中 .class 檔案的內容。
這些 Agents 擁有的這些 JVM 的功能許可權, 是在 JVM 內執行的 Java Code 所無法獲取的, 他們能用來比如修改執行中的原始碼, 效能分析等。 像 JRebel 工具就是用了這些功能達到的效果。
傳遞一個 Agent Lib 給 JVM, 通過新增 agentlib:libname[=options] 格式的啟動引數即可辦到。像上面的遠端除錯我們用的就是 ""-agentlib:jdwp=... ""來引入 jdwp 這個 Agent 的。
jdwp 是一個 JVM 特定的 JDWP(Java Debug Wire Protocol) 可選實現,用來定義除錯者與執行JVM之間的通訊,它的是通過 JVM 本地庫的 jdwp.so 或者 jdwp.dll 支援實現的。
簡單來說, jdwp agent 會建立執行應用的 JVM 和除錯者(本地或者遠端)之間的橋樑。既然他是一個Agent Library, 它就有能力攔截執行的程式碼。
在 JVM 架構裡, debugging 功能在 JVM 本身的內部是找不到的,它是一種抽象到外部工具的方式(也稱作除錯者 debugger)。這些除錯工具或者執行在 JVM 的本地 或者在遠端。這是一種解耦,模組化的架構。