集群前后臺(tái)協(xié)議需要做一些修改一睁,我負(fù)責(zé)jdbc這邊的修改厅翔。按照協(xié)議內(nèi)容修改完代碼之后卻面臨一個(gè)測(cè)試的問題:修改后的后臺(tái)又部署在北京呢簸,但北京并不是所有的機(jī)器都對(duì)天津這邊開放兽泄,只給提供一臺(tái)機(jī)器A漓概,就是集群的服務(wù)器。
這樣的話,就無法創(chuàng)建節(jié)點(diǎn)的連接病梢,測(cè)試沒有辦法進(jìn)行胃珍。
一開始用了最簡(jiǎn)單的辦法,把打好的jar包通過遠(yuǎn)程ssh放到A上面蜓陌,再通過ssh去跑用例觅彰,打印結(jié)果看看正確與否。但是這樣的效率真的太低了钮热,每做一次修改都要打包填抬、部署、運(yùn)行隧期、分析log飒责。于是借這個(gè)機(jī)會(huì)學(xué)習(xí)一下java程序的遠(yuǎn)程調(diào)試。以下為總結(jié)仆潮。
一些概念
JDPA: java平臺(tái)調(diào)試架構(gòu)
JVMTI: JVM端調(diào)試接口
JDI: java端調(diào)試接口
JDWP: java調(diào)試網(wǎng)絡(luò)協(xié)議
JPDA 定義了一套如何開發(fā)調(diào)試工具的接口和規(guī)范宏蛉。
JPDA 由三個(gè)獨(dú)立的模塊 JVMTI、JDWP性置、JDI 組成拾并。
調(diào)試者通過 JDI 發(fā)送接受調(diào)試命令。
JDWP 定義調(diào)試者和被調(diào)試者交流數(shù)據(jù)的格式鹏浅。
JVMTI 可以控制當(dāng)前虛擬機(jī)運(yùn)行狀態(tài)辟灰。
上圖中的前端工具就是我們要用到的調(diào)試工具。如JDB篡石、Eclipse等等。這些工具實(shí)現(xiàn)了JDI接口西采,通過這些工具我們可以達(dá)到在命令行或者圖形界面下調(diào)試的目的凰萨。
關(guān)于這部分,只是簡(jiǎn)單的了解一下概念,更多的關(guān)于JDPA的介紹:JDPA體系
使用JDB進(jìn)行本地調(diào)試
JDB 是jdk自帶的一個(gè)調(diào)試工具胖眷,用于命令行下調(diào)試java程序
jdb.exe就位于jdk安裝目錄的bin目錄下武通,安裝好jdk并設(shè)置好環(huán)境變量之后就可以愉快的使用jdb了。
C:\Users\kyu>jdb -help
用法: jdb <options> <class> <arguments>
其中, 選項(xiàng)包括:
-help 輸出此消息并退出
-sourcepath <由 ";" 分隔的目錄>
要在其中查找源文件的目錄
-attach <address>
使用標(biāo)準(zhǔn)連接器附加到指定地址處正在運(yùn)行的 VM
-listen <address>
等待正在運(yùn)行的 VM 使用標(biāo)準(zhǔn)連接器在指定地址處連接
-listenany
等待正在運(yùn)行的 VM 使用標(biāo)準(zhǔn)連接器在任何可用地址處連接
-launch
立即啟動(dòng) VM 而不是等待 'run' 命令
-listconnectors 列出此 VM 中的可用連接器
-connect <connector-name>:<name1>=<value1>,...
使用所列參數(shù)值通過指定的連接器連接到目標(biāo) VM
-dbgtrace [flags] 輸出信息供調(diào)試jdb
-tclient 在 HotSpot(TM) 客戶機(jī)編譯器中運(yùn)行應(yīng)用程序
-tserver 在 HotSpot(TM) 服務(wù)器編譯器中運(yùn)行應(yīng)用程序
轉(zhuǎn)發(fā)到被調(diào)試進(jìn)程的選項(xiàng):
-v -verbose[:class|gc|jni]
啟用詳細(xì)模式
-D<name>=<value> 設(shè)置系統(tǒng)屬性
-classpath <由 ";" 分隔的目錄>
列出要在其中查找類的目錄
-X<option> 非標(biāo)準(zhǔn)目標(biāo) VM 選項(xiàng)
<class> 是要開始調(diào)試的類的名稱
<arguments> 是傳遞到 <class> 的 main() 方法的參數(shù)
要獲得命令的幫助, 請(qǐng)?jiān)趈db提示下鍵入 'help'
上面是啟動(dòng)JDB的語法說明珊搀。
現(xiàn)假設(shè)運(yùn)行程序的工程目錄如下:
JDBTest
|----bin(編譯生成的class文件)
| |----*.class
|----src(源文件)
| |----*.java
|----lib(依賴的第三方j(luò)ar)
| |----*.jar
開始啟動(dòng)JDB調(diào)試:
Y:\project\JavaProject\JDBTest>jdb -classpath ./bin/;./lib/* -sourcepath ./src/ test.JDBTest
注意冶忱,如果有多個(gè)文件,windows下使用 ";" 分隔每個(gè)文件或目錄境析,Linux下使用 ":" 分隔每個(gè)文件或目錄
-classpath
指定了類路徑囚枪,-sourcepath
指定了源文件的路徑
回車,出現(xiàn)如下信息:
Y:\project\JavaProject\JDBTest>jdb -classpath ./bin/;./lib/* -sourcepath ./src/ test.JDBTest
正在初始化jdb...
>
此時(shí)劳淆,JDB調(diào)試器等待用戶的輸入链沼,輸入help
,出現(xiàn)如下信息:
Y:\project\JavaProject\JDBTest>jdb -classpath ./bin/;./lib/* -sourcepath ./src/ test.JDBTest
正在初始化jdb...
> help
** 命令列表 **
connectors -- 列出此 VM 中可用的連接器和傳輸
run [class [args]] -- 開始執(zhí)行應(yīng)用程序的主類
threads [threadgroup] -- 列出線程
thread <thread id> -- 設(shè)置默認(rèn)線程
suspend [thread id(s)] -- 掛起線程 (默認(rèn)值: all)
resume [thread id(s)] -- 恢復(fù)線程 (默認(rèn)值: all)
where [<thread id> | all] -- 轉(zhuǎn)儲(chǔ)線程的堆棧
wherei [<thread id> | all]-- 轉(zhuǎn)儲(chǔ)線程的堆棧, 以及 pc 信息
up [n frames] -- 上移線程的堆棧
down [n frames] -- 下移線程的堆棧
kill <thread id> <expr> -- 終止具有給定的異常錯(cuò)誤對(duì)象的線程
interrupt <thread id> -- 中斷線程
print <expr> -- 輸出表達(dá)式的值
dump <expr> -- 輸出所有對(duì)象信息
eval <expr> -- 對(duì)表達(dá)式求值 (與 print 相同)
set <lvalue> = <expr> -- 向字段/變量/數(shù)組元素分配新值
locals -- 輸出當(dāng)前堆棧幀中的所有本地變量
classes -- 列出當(dāng)前已知的類
class <class id> -- 顯示已命名類的詳細(xì)資料
methods <class id> -- 列出類的方法
fields <class id> -- 列出類的字段
threadgroups -- 列出線程組
threadgroup <name> -- 設(shè)置當(dāng)前線程組
stop in <class id>.<method>[(argument_type,...)]
-- 在方法中設(shè)置斷點(diǎn)
stop at <class id>:<line> -- 在行中設(shè)置斷點(diǎn)
clear <class id>.<method>[(argument_type,...)]
-- 清除方法中的斷點(diǎn)
clear <class id>:<line> -- 清除行中的斷點(diǎn)
clear -- 列出斷點(diǎn)
catch [uncaught|caught|all] <class id>|<class pattern>
-- 出現(xiàn)指定的異常錯(cuò)誤時(shí)中斷
ignore [uncaught|caught|all] <class id>|<class pattern>
-- 對(duì)于指定的異常錯(cuò)誤, 取消 'catch'
watch [access|all] <class id>.<field name>
-- 監(jiān)視對(duì)字段的訪問/修改
unwatch [access|all] <class id>.<field name>
-- 停止監(jiān)視對(duì)字段的訪問/修改
trace [go] methods [thread]
-- 跟蹤方法進(jìn)入和退出沛鸵。
-- 除非指定 'go', 否則掛起所有線程
trace [go] method exit | exits [thread]
-- 跟蹤當(dāng)前方法的退出, 或者所有方法的退出
-- 除非指定 'go', 否則掛起所有線程
untrace [methods] -- 停止跟蹤方法進(jìn)入和/或退出
step -- 執(zhí)行當(dāng)前行
step up -- 一直執(zhí)行, 直到當(dāng)前方法返回到其調(diào)用方
stepi -- 執(zhí)行當(dāng)前指令
next -- 步進(jìn)一行 (步過調(diào)用)
cont -- 從斷點(diǎn)處繼續(xù)執(zhí)行
list [line number|method] -- 輸出源代碼
use (或 sourcepath) [source file path]
-- 顯示或更改源路徑
exclude [<class pattern>, ... | "none"]
-- 對(duì)于指定的類, 不報(bào)告步驟或方法事件
classpath -- 從目標(biāo) VM 輸出類路徑信息
monitor <command> -- 每次程序停止時(shí)執(zhí)行命令
monitor -- 列出監(jiān)視器
unmonitor <monitor#> -- 刪除監(jiān)視器
read <filename> -- 讀取并執(zhí)行命令文件
lock <expr> -- 輸出對(duì)象的鎖信息
threadlocks [thread id] -- 輸出線程的鎖信息
pop -- 通過當(dāng)前幀出棧, 且包含當(dāng)前幀
reenter -- 與 pop 相同, 但重新進(jìn)入當(dāng)前幀
redefine <class id> <class file name>
-- 重新定義類的代碼
disablegc <expr> -- 禁止對(duì)象的垃圾收集
enablegc <expr> -- 允許對(duì)象的垃圾收集
!! -- 重復(fù)執(zhí)行最后一個(gè)命令
<n> <command> -- 將命令重復(fù)執(zhí)行 n 次
# <command> -- 放棄 (無操作)
help (或 ?) -- 列出命令
version -- 輸出版本信息
exit (或 quit) -- 退出調(diào)試器
<class id>: 帶有程序包限定符的完整類名
<class pattern>: 帶有前導(dǎo)或尾隨通配符 ('*') 的類名
<thread id>: 'threads' 命令中報(bào)告的線程編號(hào)
<expr>: Java(TM) 編程語言表達(dá)式括勺。
支持大多數(shù)常見語法。
可以將啟動(dòng)命令置于 "jdb.ini" 或 ".jdbrc" 中
位于 user.home 或 user.dir 中
>
上面的幫助信息說明了如何進(jìn)行JDB調(diào)試曲掰,解釋一下其中的幾個(gè):
step: -- 執(zhí)行當(dāng)前行 相當(dāng)于Eclipse中的F5
step up: -- 一直執(zhí)行, 直到當(dāng)前方法返回到其調(diào)用方 相當(dāng)于Eclipse中的F7
next: -- 步進(jìn)一行 (步過調(diào)用) 相當(dāng)于Eclipse中的F6
cont: -- 從斷點(diǎn)處繼續(xù)執(zhí)行 相當(dāng)于Eclipse中的F8
此時(shí)疾捍,繼續(xù)輸入:
> stop at test.JDBTest:7
正在延遲斷點(diǎn)test.JDBTest:7。
將在加載類后設(shè)置栏妖。
> run
運(yùn)行test.JDBTest
設(shè)置未捕獲的java.lang.Throwable
設(shè)置延遲的未捕獲的java.lang.Throwable
>
VM 已啟動(dòng): 設(shè)置延遲的斷點(diǎn)test.JDBTest:7
斷點(diǎn)命中: "線程=main", test.JDBTest.main(), 行=7 bci=0
7 JDBTest jdbTest = new JDBTest();
main[1]
stop at test.JDBTest:7 表示在這個(gè)類文件的第7行處打一個(gè)斷點(diǎn)
接著乱豆,輸入run,就開始進(jìn)入調(diào)試步驟了〉赘纾現(xiàn)在可以輸入上面幫助中的語法來了解當(dāng)前程序的執(zhí)行情況了咙鞍。一試便知
注意, 若想要在調(diào)試時(shí)能夠正常輸出調(diào)試信息如變量值等等,需要在編譯java文件時(shí)指定 -g 參數(shù)趾徽,否則無法獲得其運(yùn)行時(shí)的調(diào)試信息
另外续滋,使用list可以打印當(dāng)前斷點(diǎn)處的源代碼,如果沒有在啟動(dòng)JDB時(shí)指定源代碼路徑 -sourcepath ./src/ 孵奶,那么會(huì)提示沒有源代碼信息疲酌,無法輸出。此時(shí)可以使用命令 use ./src/ 來指定源代碼路徑了袁,再使用list命令時(shí)可以正常打印了朗恳。
以上就是使用JDB調(diào)試本地程序的方法,具體的使用可根據(jù)實(shí)際情況參照語法說明去執(zhí)行载绿。
使用JDB進(jìn)行遠(yuǎn)程調(diào)試
如果程序不是運(yùn)行在本機(jī)粥诫,而是在其他機(jī)器或者現(xiàn)場(chǎng)的時(shí)候,可以使用java提供的遠(yuǎn)程調(diào)試功能崭庸。
假設(shè)程序現(xiàn)運(yùn)行在主機(jī) 192.168.101.72 這臺(tái)機(jī)器上怀浆,該機(jī)器為linux環(huán)境谊囚,且只可以通過ssh作為一個(gè)普通用戶連接。我們想要在自己的機(jī)器上調(diào)試運(yùn)行在192.168.101.72這臺(tái)機(jī)器上的程序执赡。
啟動(dòng)要調(diào)試的程序
在192.168.101.72這臺(tái)主機(jī)上以下面的方式啟動(dòng)java程序:還是以JDBTest為例
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8899 -classpath ./bin/:./lib/* test.JDBTest
此時(shí)镰踏,命令行輸出
Listening for transport dt_socket at address: 8899
并處于等待狀態(tài)
下面是幾個(gè)參數(shù)的解釋:
-Xdebug 啟用調(diào)試特性。
-Xrunjdwp:<sub-options> 在目標(biāo) VM 中加載 JDWP 實(shí)現(xiàn)沙合。它通過傳輸和 JDWP 協(xié)議與獨(dú)立的調(diào)試器應(yīng)用程序通信奠伪。下面介紹一些特定的子選項(xiàng)。
從 Java V5 開始首懈,您可以使用 -agentlib:jdwp 選項(xiàng)绊率,而不是 -Xdebug 和 -Xrunjdwp。但如果連接到 V5 以前的 VM猜拾,只能選擇 -Xdebug 和 -Xrunjdwp即舌。下面簡(jiǎn)單描述 -Xrunjdwp 子選項(xiàng)。
transport 這里通常使用套接字傳輸挎袜。但是在 Windows 平臺(tái)上也可以使用共享內(nèi)存?zhèn)鬏敗?/p>
server 如果值為 y顽聂,目標(biāo)應(yīng)用程序監(jiān)聽將要連接的調(diào)試器應(yīng)用程序。否則盯仪,它將連接到特定地址上的調(diào)試器應(yīng)用程序紊搪。
address 這是連接的傳輸?shù)刂贰H绻?wù)器為 n全景,將嘗試連接到該地址上的調(diào)試器應(yīng)用程序耀石。否則,將在這個(gè)端口監(jiān)聽連接爸黄。
suspend 如果值為 y滞伟,目標(biāo) VM 將暫停,直到調(diào)試器應(yīng)用程序進(jìn)行連接炕贵。
本機(jī)連接遠(yuǎn)程程序并啟動(dòng)調(diào)試
在本機(jī)上命令行下輸入:
jdb -connect com.sun.jdi.SocketAttach:hostname=192.168.101.72,port=8899
然后就進(jìn)入了調(diào)試界面梆奈,你可以像調(diào)試本機(jī)程序那樣使用JDB的一些命令來調(diào)試了。當(dāng)退出調(diào)試程序時(shí)称开,遠(yuǎn)程主機(jī)上的程序也就退出了亩钟。
使用Eclipse進(jìn)行遠(yuǎn)程調(diào)試
可以使用Eclipse進(jìn)行遠(yuǎn)程調(diào)試,就上上面使用JDB一樣鳖轰。
啟動(dòng)要調(diào)試的程序
與JDB遠(yuǎn)程調(diào)試一樣清酥,啟動(dòng)遠(yuǎn)程主機(jī)上的程序:
java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=8899 -classpath ./bin/:./lib/* test.JDBTest
本機(jī)啟動(dòng)Eclipse進(jìn)行調(diào)試
首先要右鍵工程->java compiler
上圖中的幾個(gè)選項(xiàng)最好全部打勾,否則調(diào)試時(shí)會(huì)出現(xiàn)無法打斷點(diǎn)或者獲取不到行號(hào)等問題(關(guān)于這幾個(gè)選項(xiàng)的含義在之前的總結(jié)中有提到)
接著蕴侣,右鍵工程->Debug As->Run Configurations, 在出現(xiàn)的對(duì)話框中選擇Remote Java Application, 右鍵->New, 出現(xiàn)如下界面:
在Connect頁中焰轻,選擇對(duì)應(yīng)的java 工程,Connection Type選擇 Socket Attach昆雀,然后填寫遠(yuǎn)程主機(jī)的ip和端口鹦马,這里應(yīng)該填寫192.168.101.72和8899胧谈。
在Source頁中可以添加源代碼,如用到的第三方j(luò)ar的源代碼或者引用的工程荸频,調(diào)試時(shí)就可以進(jìn)入到這部分代碼查看。在Common頁可以設(shè)置編碼的配置客冈。
接下來點(diǎn)擊Debug按鈕旭从,就可以愉快的在本機(jī)調(diào)試遠(yuǎn)程程序了,就像調(diào)試本地程序那樣场仲。只不過可能有一點(diǎn)一點(diǎn)慢和悦,不過比打Log的方式要好很多了。