本文分享一下在使用或者學(xué)習(xí)開(kāi)源項(xiàng)目源碼的過(guò)程中的一些經(jīng)驗(yàn)技巧。
因?yàn)槲易罱谘芯?Apache Pulsar 這款消息隊(duì)列,所以就以這個(gè)項(xiàng)目為例, 不過(guò)本文介紹的都是通用的技巧,完全可以用在其他大型開(kāi)源項(xiàng)目中 。
下面就來(lái)具體介紹一些技巧,主要分兩部分:
第一部分是文檔篇,即能夠哪里能夠獲取有效的信息解決問(wèn)題;
第二部分是實(shí)操篇,即如何高效打斷點(diǎn)或借助工具理解源碼。
一、文檔檢索技巧
想學(xué)習(xí)了解一個(gè)開(kāi)源項(xiàng)目,文檔可以幫我們解決大部分問(wèn)題。當(dāng)然我這里所說(shuō)的不單單指官網(wǎng)文檔,還包括 issue、PR、源碼中的注釋和單元測(cè)試,這些地方都可以獲得大量有用的信息,所以我把它們統(tǒng)稱為文檔,下面我們從最簡(jiǎn)單的開(kāi)始。
1、官網(wǎng)文檔,著重 quickstart 和 concept 部分 。
官網(wǎng)文檔無(wú)疑是最權(quán)威的資料來(lái)源,不過(guò)官網(wǎng)文檔的問(wèn)題是內(nèi)容太多太全面,適合遇到問(wèn)題或需求時(shí)當(dāng)做功能手冊(cè)去查閱。
所以官網(wǎng)的內(nèi)容需要選擇性地學(xué)習(xí),我建議優(yōu)先著重兩個(gè)部分:
一是 quickstart 部分,也就是教你如何快速部署一個(gè) demo 服務(wù);二是 concept 部分,也就是名詞解釋、核心功能介紹等內(nèi)容。
快速部署 demo 服務(wù)不用說(shuō)了,是我們學(xué)習(xí)新技術(shù)的第一步,一般會(huì)被放在文檔的第一章;而功能/名詞的解釋是我們接下來(lái)順暢地學(xué)習(xí)進(jìn)階資料或參與社區(qū)討論的重要鋪墊。
對(duì) Pulsar 這樣一個(gè)消息隊(duì)列來(lái)說(shuō),收發(fā)消息顯然是核心功能,所以官網(wǎng) Concepts and Architecture 部分中的 Messaging 章節(jié)顯然是很重要的,詳細(xì)介紹了 Pulsar 中諸如訂閱模式、死信隊(duì)列等關(guān)鍵功能:
我在前文 Apache Pulsar 架構(gòu)設(shè)計(jì) 介紹到 Pulsar 采用存算分離的架構(gòu),存儲(chǔ)層依靠 Apache Bookkeeper。所以如果你的目標(biāo)是學(xué)習(xí) Pulsar,那么 Bookkeeper 的官網(wǎng)文檔也是需要閱讀的,因?yàn)?Pulsar 中的很多功能都會(huì)和 Bookkeeper 交互。
可以在本地啟一個(gè) Bookkeeper 集群用 client 玩一玩,閱讀了解一下 Bookkeeper 中的專業(yè)術(shù)語(yǔ),有助于理解 Pulsar 中的一些設(shè)計(jì)。
2、看完文檔看單元測(cè)試用例,輔助我們準(zhǔn)確理解每個(gè)功能的預(yù)期行為 。
一般成熟開(kāi)源項(xiàng)目的測(cè)試用例比較完備,會(huì)覆蓋所有關(guān)鍵功能的預(yù)期行為,所以單測(cè)用例其實(shí)也是很好的學(xué)習(xí)資料,和文檔搭配食用效果最佳。
比方說(shuō),有時(shí)候文檔用文字描述某個(gè)功能可能會(huì)比較繁瑣,讓人看的云里霧里,又或者文檔中并沒(méi)有介紹一些技術(shù)設(shè)計(jì)的細(xì)節(jié)。
遇到這種情況,我們大概率可以在單測(cè)文件中找到對(duì)應(yīng)的功能測(cè)試代碼,根據(jù)測(cè)試代碼很容易反推功能,正所謂「talk is cheap, show me the code」。
舉個(gè)例子,有一次我看到 consumer 打出一條關(guān)于epoch
的日志,我在分布式選主的場(chǎng)景倒是聽(tīng)說(shuō)過(guò)這個(gè)名詞,不過(guò)顯然消費(fèi)消息和分布式選主沒(méi)什么關(guān)系,所以這個(gè)epoch
到底是干什么的?
文檔里沒(méi)找到答案,這應(yīng)該是一個(gè)具體實(shí)現(xiàn)中的術(shù)語(yǔ),所以我就在源碼中搜索包含testEpoch
和epochTest
這兩個(gè)關(guān)鍵詞的函數(shù)名,發(fā)現(xiàn)了幾個(gè)測(cè)試用例:
PS:測(cè)試函數(shù)名的 test 關(guān)鍵字可能在開(kāi)頭也可能在最后,所以需要都搜一下。
瀏覽了一下這幾個(gè)測(cè)試用例的內(nèi)容就大致理解了,原來(lái)這個(gè)epoch
是消息重投遞功能(redelivery)中的一個(gè)術(shù)語(yǔ),主要用于防止重復(fù)消費(fèi)消息。
3、善用 GitHub,從項(xiàng)目的 issue/PR/wiki 列表獲取有效信息 。
首先,issue 列表不用多說(shuō)了,如果你在使用軟件的過(guò)程中遇到了問(wèn)題,首先考慮的就是去 issue 列表搜索。
雖然有時(shí)候搜出來(lái)的并不是直接的答案,但多換關(guān)鍵詞搜幾次,大概率就能找到一些思路解決問(wèn)題了。
另外, PR 信息可以幫助我們了解某些代碼片段的上下文背景 。
舉個(gè)例子,比如你閱讀某段代碼時(shí)有疑惑,不明白這個(gè)代碼的目的是什么,那么可以在 IDEA 中的代碼左側(cè)單擊右鍵,打開(kāi)「Annotate with Git Blame」就可看到這段代碼是誰(shuí)在什么時(shí)候添加上去的:
然后把鼠標(biāo)懸停在作者昵稱上兩秒,就會(huì)彈出這個(gè)代碼被合進(jìn) master 分支時(shí)的 PR 標(biāo)題和鏈接:
18260
就是這個(gè) PR 的編號(hào),點(diǎn)擊即可跳轉(zhuǎn)到對(duì)應(yīng)的 PR 頁(yè)面:
可以看到這個(gè) PR 是用來(lái)修復(fù)18241
號(hào) issue 的,在18241
號(hào) issue 中詳細(xì)描述了 bug 信息及復(fù)現(xiàn)方法:
有了這些上下文信息,就可以避免我們閱讀源碼時(shí)的障礙了。
最后, wiki 頁(yè)面可以幫我們了解一些重要的功能設(shè)計(jì)或改動(dòng) 。
拿 Pulsar 來(lái)說(shuō),如果需要做比較重要的改動(dòng),需要提出一個(gè) PIP 提案(Pulsar Improvement Proposal),也就是一個(gè)專門講解背景信息、設(shè)計(jì)思路的文檔。
而這些 PIP 文檔就收集在 wiki 頁(yè)面:
所以在了解某個(gè)功能模塊的設(shè)計(jì)思路時(shí),可以先去 wiki 頁(yè)面看看是否有相關(guān)的 PIP 可供參考。
比如 Pulsar 的事務(wù)實(shí)現(xiàn),就有一個(gè)專門的 PIP 詳細(xì)介紹了設(shè)計(jì)思路,結(jié)合 PIP 的思路指引去學(xué)習(xí)源碼就會(huì)容易很多:
我個(gè)人覺(jué)得,好的 PIP 結(jié)合源碼,帶我們把一個(gè)功能從討論設(shè)計(jì)做到落地實(shí)現(xiàn),這就是很好的教科書(shū)呀,多花精力去研究,肯定會(huì)有所收獲的。
以上就是最常用的有效信息的獲取途徑,如果你在學(xué)習(xí)使用開(kāi)源項(xiàng)目時(shí)遇到問(wèn)題,那么可以嘗試上述的方法去尋找答案。
當(dāng)然,熟練掌握進(jìn)行信息檢索的工具進(jìn)行高效檢索也是重要的技能,比如說(shuō) IDEA 的各種搜索、GitHub issue/PR 的搜索語(yǔ)法,這些技巧網(wǎng)上可以很容易搜到,我就不贅述了。
二、源碼閱讀技巧
想真正了解一個(gè)項(xiàng)目,看源碼肯定是逃不掉的一環(huán)。閱讀源碼的好處不用多說(shuō)了,但閱讀源碼肯定會(huì)花費(fèi)大量時(shí)間,而且這個(gè)過(guò)程不會(huì)很輕松。
你想嘛,成熟的開(kāi)源項(xiàng)目經(jīng)過(guò)多年的發(fā)展,功能不斷演進(jìn),很多人往里面寫過(guò)代碼,恐怕沒(méi)人能保證自己完全了解系統(tǒng)的每個(gè)細(xì)節(jié)。我們閱讀源碼,就好比探索一座龐大的城市,很容易迷失在某個(gè)犄角旮旯。
對(duì)于這個(gè)問(wèn)題,我可以分享一些小技巧。
技巧一、不建議看「死代碼」,建議在調(diào)試實(shí)際問(wèn)題的過(guò)程中理解代碼 。
換句話說(shuō),不要拿著代碼硬讀,最好是通過(guò)動(dòng)態(tài)調(diào)試來(lái)研究每個(gè)功能中做了什么。
拿 Pulsar 舉例,我們可以在命令行啟動(dòng) standalone 模式的 Pulsar broker:
$ bin/pulsar standalone
然后用 Java client 創(chuàng)建一個(gè) producer 發(fā)送一條消息:
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://localhost:6650")
.build();
Producer<byte[]> producer = client.newProducer()
.topic("testTopic")
.create();
MessageId messageId1 = producer.send(("hello1").getBytes());
client.close();
我們就可以調(diào)試這個(gè)簡(jiǎn)單的場(chǎng)景,看看 producer 是如何創(chuàng)建的,消息是如何發(fā)送并存儲(chǔ)在 Pulsar 中的。
但如果想跟蹤調(diào)試這段代碼,會(huì)遇到一些問(wèn)題:
第一個(gè)問(wèn)題是,我們自己的項(xiàng)目是通過(guò) Maven 引入 client 包的,如果進(jìn)入這些包看到的是反編譯的 class 文件,無(wú)法直接看到源碼。就算 IDEA 可以直接幫我們下載源碼,但如果我們?cè)趶氖?client 的開(kāi)發(fā),需要 master 分支的最新版代碼,這和上傳到 Maven 的源碼還是不一樣。
這個(gè)問(wèn)題比較容易解決,我們直接從 GitHub 下載源碼,在 client 包里面創(chuàng)建一個(gè) test 文件寫邏輯,這樣就可以調(diào)試最新的 client 代碼了。
第二個(gè)問(wèn)題比較棘手,我們想調(diào)通整個(gè) Pulsar 發(fā)送消息的流程,那么這里面肯定要涉及 Pulsar client 和 Pulsar broker 的交互,而 broker 是通過(guò)命令行啟動(dòng)的,我如何調(diào)試 broker 里面的代碼呢?
我們可以觀察一下,bin/pulsar
這個(gè)文件其實(shí)就是個(gè) shell 腳本,可以找到這樣一段代碼:
elif [ $COMMAND == "standalone" ]; then
PULSAR_LOG_FILE=${PULSAR_LOG_FILE:-"pulsar-standalone.log"}
exec $JAVA $LOG4J2_SHUTDOWN_HOOK_DISABLED $OPTS ${ZK_OPTS} -Dpulsar.log.file=$PULSAR_LOG_FILE -Dpulsar.config.file=$PULSAR_STANDALONE_CONF org.apache.pulsar.PulsarStandaloneStarter $@
standalone
命令其實(shí)就是運(yùn)行java
命令,輸入一大堆參數(shù),加載了一堆 jar 包,最終啟動(dòng)了PulsarStandaloneStarter
這個(gè)類,所以我們可以使用 JVM 遠(yuǎn)程調(diào)試功能 。
IDE 就給我們提供了 Remote JVM Debug 功能:
我新建一個(gè)遠(yuǎn)程調(diào)試,參數(shù)填默認(rèn)的就行,這里 IDE 給我們自動(dòng)生成了一段 JVM 參數(shù):
我們把這段 JVM 參數(shù)復(fù)制,把其中的suspend=n
改成suspend=y
,然后修改bin/pulsar
文件,把這段參數(shù)添加到standalone
模式的啟動(dòng)參數(shù)中:
elif [ $COMMAND == "standalone" ]; then
# 添加調(diào)試參數(shù),注意 suspend=y
OPTS="${OPTS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:5005"
PULSAR_LOG_FILE=${PULSAR_LOG_FILE:-"pulsar-standalone.log"}
exec $JAVA $LOG4J2_SHUTDOWN_HOOK_DISABLED $OPTS ${ZK_OPTS} -Dpulsar.log.file=$PULSAR_LOG_FILE -Dpulsar.config.file=$PULSAR_STANDALONE_CONF org.apache.pulsar.PulsarStandaloneStarter $@
這樣,我們本地命令行再執(zhí)行執(zhí)行bin/pulsar standalone
時(shí)就會(huì)掛起:
$ bin/pulsar standalone --num-bookies 3
Listening for transport dt_socket at address: 5005
此時(shí),你在 IDE 里可以給代碼隨意打斷點(diǎn),點(diǎn)擊 debug 按鈕后 broker 才會(huì)啟動(dòng),走到斷點(diǎn)處將暫停,我們可以在 IDE 中查看變量、堆棧等信息。
這樣我們就能在 IDE 中同時(shí)調(diào)試 client 端和 broker 端的代碼了。
但是需要注意的是, 進(jìn)行遠(yuǎn)程調(diào)試的源代碼必須和命令行啟動(dòng)的 broker 一致 ,否則會(huì)導(dǎo)致調(diào)試時(shí)行數(shù)對(duì)不上的問(wèn)題。
如果出現(xiàn)源碼對(duì)不上的情況,可以在 pulsar 項(xiàng)目的根目錄用 maven 重新編譯當(dāng)前的源碼:
$ mvn package -DskipTests -Dlicense.skip=true
編譯好的二進(jìn)制包在distribution/server/target
中,我們?cè)谛碌陌械?code>bin/pulsar腳本添加遠(yuǎn)程 debug 的參數(shù),然后再次啟動(dòng)即可順利調(diào)試。
-
源碼
+關(guān)注
關(guān)注
8文章
632瀏覽量
29111 -
開(kāi)源項(xiàng)目
+關(guān)注
關(guān)注
0文章
36瀏覽量
7163
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論