技巧二、多猜,多搜索,可以在底層庫(標(biāo)準(zhǔn)庫、網(wǎng)絡(luò)框架等)打條件斷點(diǎn)過篩選出關(guān)鍵流程 。
這句話其實(shí)是高效 debug 的關(guān)鍵。初看源碼時(shí)「猜」是很重要且很有效的手段,結(jié)合 IDE 的搜索功能,能夠幫我們快速定位關(guān)鍵代碼。
為什么底層庫適合打斷點(diǎn)呢?因?yàn)槌隹创箜?xiàng)目的代碼很難搞清楚其中的細(xì)節(jié),加上各種異步、多線程的操作,很容易把代碼「跟丟」。如果把斷點(diǎn)打在底層庫的接口/方法上,就可以根據(jù)調(diào)用棧分析調(diào)用過程。
當(dāng)然,底層庫被調(diào)用的次數(shù)比較多,可能出現(xiàn)很多無關(guān)的調(diào)用,所以要結(jié)合條件斷點(diǎn)來過濾掉無關(guān)的調(diào)用。
還是用 Pulsar 舉例,我現(xiàn)在想探究 producer 發(fā)送消息的流程,那么 producer 和 broker 之間的網(wǎng)絡(luò)通信過程就是一個(gè)重要的切入點(diǎn)。
首先發(fā)現(xiàn) Pulsar 的網(wǎng)絡(luò)協(xié)議使用的是 protobuf,而且注意到PulsarApi.proto
這個(gè)文件中有一個(gè)BaseCommand
定義:
message BaseCommand {
enum Type {
CONNECT = 2;
SUBSCRIBE = 4;
PRODUCER = 5;
SEND = 6;
SEND_RECEIPT= 7;
MESSAGE = 9;
ACK = 10;
PING = 18;
PONG = 19;
...
}
required Type type = 1;
optional CommandConnect connect = 2;
optional CommandConnected connected = 3;
...
}
又發(fā)現(xiàn) Pulsar 底層靠 netty 框架實(shí)現(xiàn)網(wǎng)絡(luò)通信,那么我們可以大膽猜測(cè), 源碼里肯定有一個(gè)大 switch 語句 ,來根據(jù) command 里面的 type 分類處理對(duì)應(yīng)的 command。
所以我們可以全局搜索一下case SEND_RECEIPT
,就找到了PulsarDecoder
這個(gè)文件:
這里會(huì)根據(jù)不同的 command type 調(diào)用不同的 handle 函數(shù),所以可以認(rèn)為這里是 Pulsar 關(guān)鍵功能的入口。
而且注意這是 common 包,也就是說 client 和 broker 都會(huì)依賴這個(gè)包, 所以斷點(diǎn)打在 switch 這里就可以看到 client 和 broker 的網(wǎng)絡(luò)交互 ,每次跳轉(zhuǎn)的 case 就是網(wǎng)絡(luò)命令的交互順序:
PS:因?yàn)?ping/pong 心跳消息在調(diào)試時(shí)很煩人,所以我們可以通過條件斷點(diǎn)跳過心跳消息。另外,我們需要把 client 里面的各種 timeout 都調(diào)大一些,避免調(diào)試時(shí)出現(xiàn)超時(shí)的錯(cuò)誤。
這樣,啟動(dòng)我們的測(cè)試用例,僅僅通過這一個(gè)斷點(diǎn),就能搞明白 Pulsar 發(fā)消息的流程了:
當(dāng)然,如果你想探究每一步具體做了什么,就跳進(jìn)具體的 handle 函數(shù)里一步步調(diào)試即可。
技巧三、利用各種可視化工具 。
你比如,上面說的網(wǎng)絡(luò)通信過程,我們知道了 produce 一條消息的流程,但每條 protobuf 數(shù)據(jù)包里面到底存了什么信息呢?
關(guān)于這個(gè)問題,社區(qū)有大佬寫了一個(gè) lua 腳本, 可以用 wireshark 解析 Pulsar 協(xié)議格式 ,具體說明在這里:
https://github.com/apache/pulsar/tree/master/wireshark
按照說明配置并啟動(dòng) wireshark 之后,可以使用如下過濾命令過濾掉無關(guān)的數(shù)據(jù)包:
tcp.port eq 6650 and pulsar and protobuf.field.name ne "ping" and protobuf.field.name ne "pong"
接下來啟動(dòng) standalone,通過 Java client 發(fā)送一條消息,就可以在 wireshark 抓到 10 個(gè)數(shù)據(jù)包,和剛才通過 debug 得到的流程是一樣的:
同時(shí),我們還可以查看每個(gè)包的具體數(shù)據(jù),比如PARTITITONED_METADATA
命令就是在查詢 topic 對(duì)應(yīng)的 partition 有多少,因?yàn)檫@里是個(gè)非分區(qū)的 topic,所以PARTITITONED_METADATA_RESPONSE
返回了 0:
再比如LOOKUP
命令用來查詢 broker 的 URL,因?yàn)槲覀儐?dòng)的 standalone 只有一個(gè) broker,所以LOOKUP_RESPONSE
返回的只有一個(gè) URL:
在真實(shí)的使用場(chǎng)景中肯定有多個(gè) broker,所以這個(gè)LOOKUP_RESPONSE
應(yīng)該會(huì)返回多個(gè) broker URL。
最后看一下真正發(fā)送消息的SEND
命令里面具體有什么數(shù)據(jù):
可以看到這里面有 producer_name, sequence_id 等數(shù)據(jù),每條消息的 sequence_id 單調(diào)遞增,用來防止由于網(wǎng)絡(luò)重傳導(dǎo)致的消息重復(fù),和 tcp 里面的 seq 差不多的原理。
另外可以看到真正的消息數(shù)據(jù)放在數(shù)據(jù)包的最后,通過一個(gè)字段記錄數(shù)據(jù)的長(zhǎng)度。
具體的玩法可以有很多,我這里就不一一列舉了,其實(shí)除了 wireshark 分析 Pulsar 的網(wǎng)絡(luò)通信, 還可以使用 zookeeper 的可視化工具查看 Pulsar 的元數(shù)據(jù) 。
比如 prettyZoo 就是一款對(duì) zookeeper 可視化的開源工具,那么我就可以在 Pulsar standalone 啟動(dòng)之后(會(huì)自動(dòng)啟動(dòng) zookeeper),讓 prettyZoo 連接到 zookeeper 的端口,很直觀地查看 zookeeper 里面的節(jié)點(diǎn)數(shù)據(jù):
這里面很多數(shù)據(jù)可能不好理解,但我們手上有源碼, 這些路徑大概率是以字符串常量的形式表現(xiàn)的,那全局搜索就行了 。
比如這個(gè)producer-name
的路徑,我們搜一下就定位出來了:
簡(jiǎn)單瀏覽一下源碼,原來是借助 zookeeper 生成全局唯一的生產(chǎn)者名字。
最后
本文也夠長(zhǎng)了,主要介紹了一些閱讀開源項(xiàng)目源碼的實(shí)用技巧,總結(jié)來說就是: 善于找資源,善于用工具 。
雖然本文是以 Pulsar 為例,但這些技巧都是通用的,可以運(yùn)用到任何比較成熟的開源項(xiàng)目上去。
如果你也有什么經(jīng)驗(yàn)分享,可以留言告訴我,掌握技巧只是漫漫長(zhǎng)路的第一步,讓我們共同在開源社區(qū)里成長(zhǎng)進(jìn)步。
-
IDE
+關(guān)注
關(guān)注
0文章
334瀏覽量
46586 -
開源
+關(guān)注
關(guān)注
3文章
3185瀏覽量
42241 -
DEBUG
+關(guān)注
關(guān)注
3文章
89瀏覽量
19808
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論