本文分享一下在使用或者學(xué)習(xí)開源項(xiàng)目源碼的過程中的一些經(jīng)驗(yàn)技巧。
因?yàn)槲易罱谘芯?Apache Pulsar 這款消息隊(duì)列,所以就以這個(gè)項(xiàng)目為例, 不過本文介紹的都是通用的技巧,完全可以用在其他大型開源項(xiàng)目中 。
下面就來具體介紹一些技巧,主要分兩部分:
第一部分是文檔篇,即能夠哪里能夠獲取有效的信息解決問題;
第二部分是實(shí)操篇,即如何高效打斷點(diǎn)或借助工具理解源碼。
一、文檔檢索技巧
想學(xué)習(xí)了解一個(gè)開源項(xiàng)目,文檔可以幫我們解決大部分問題。當(dāng)然我這里所說的不單單指官網(wǎng)文檔,還包括 issue、PR、源碼中的注釋和單元測試,這些地方都可以獲得大量有用的信息,所以我把它們統(tǒng)稱為文檔,下面我們從最簡單的開始。
1、官網(wǎng)文檔,著重 quickstart 和 concept 部分 。
官網(wǎng)文檔無疑是最權(quán)威的資料來源,不過官網(wǎng)文檔的問題是內(nèi)容太多太全面,適合遇到問題或需求時(shí)當(dāng)做功能手冊去查閱。
所以官網(wǎng)的內(nèi)容需要選擇性地學(xué)習(xí),我建議優(yōu)先著重兩個(gè)部分:
一是 quickstart 部分,也就是教你如何快速部署一個(gè) demo 服務(wù);二是 concept 部分,也就是名詞解釋、核心功能介紹等內(nèi)容。
快速部署 demo 服務(wù)不用說了,是我們學(xué)習(xí)新技術(shù)的第一步,一般會(huì)被放在文檔的第一章;而功能/名詞的解釋是我們接下來順暢地學(xué)習(xí)進(jìn)階資料或參與社區(qū)討論的重要鋪墊。
對 Pulsar 這樣一個(gè)消息隊(duì)列來說,收發(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ù)語,有助于理解 Pulsar 中的一些設(shè)計(jì)。
2、看完文檔看單元測試用例,輔助我們準(zhǔn)確理解每個(gè)功能的預(yù)期行為 。
一般成熟開源項(xiàng)目的測試用例比較完備,會(huì)覆蓋所有關(guān)鍵功能的預(yù)期行為,所以單測用例其實(shí)也是很好的學(xué)習(xí)資料,和文檔搭配食用效果最佳。
比方說,有時(shí)候文檔用文字描述某個(gè)功能可能會(huì)比較繁瑣,讓人看的云里霧里,又或者文檔中并沒有介紹一些技術(shù)設(shè)計(jì)的細(xì)節(jié)。
遇到這種情況,我們大概率可以在單測文件中找到對應(yīng)的功能測試代碼,根據(jù)測試代碼很容易反推功能,正所謂「talk is cheap, show me the code」。
舉個(gè)例子,有一次我看到 consumer 打出一條關(guān)于epoch的日志,我在分布式選主的場景倒是聽說過這個(gè)名詞,不過顯然消費(fèi)消息和分布式選主沒什么關(guān)系,所以這個(gè)epoch到底是干什么的?
文檔里沒找到答案,這應(yīng)該是一個(gè)具體實(shí)現(xiàn)中的術(shù)語,所以我就在源碼中搜索包含testEpoch和epochTest這兩個(gè)關(guān)鍵詞的函數(shù)名,發(fā)現(xiàn)了幾個(gè)測試用例:
PS:測試函數(shù)名的 test 關(guān)鍵字可能在開頭也可能在最后,所以需要都搜一下。
瀏覽了一下這幾個(gè)測試用例的內(nèi)容就大致理解了,原來這個(gè)epoch是消息重投遞功能(redelivery)中的一個(gè)術(shù)語,主要用于防止重復(fù)消費(fèi)消息。
3、善用 GitHub,從項(xiàng)目的 issue/PR/wiki 列表獲取有效信息 。
首先,issue 列表不用多說了,如果你在使用軟件的過程中遇到了問題,首先考慮的就是去 issue 列表搜索。
雖然有時(shí)候搜出來的并不是直接的答案,但多換關(guān)鍵詞搜幾次,大概率就能找到一些思路解決問題了。
另外, PR 信息可以幫助我們了解某些代碼片段的上下文背景 。
舉個(gè)例子,比如你閱讀某段代碼時(shí)有疑惑,不明白這個(gè)代碼的目的是什么,那么可以在 IDEA 中的代碼左側(cè)單擊右鍵,打開「Annotate with Git Blame」就可看到這段代碼是誰在什么時(shí)候添加上去的:
然后把鼠標(biāo)懸停在作者昵稱上兩秒,就會(huì)彈出這個(gè)代碼被合進(jìn) master 分支時(shí)的 PR 標(biāo)題和鏈接:
18260就是這個(gè) PR 的編號(hào),點(diǎn)擊即可跳轉(zhuǎn)到對應(yīng)的 PR 頁面:

可以看到這個(gè) PR 是用來修復(fù)18241號(hào) issue 的,在18241號(hào) issue 中詳細(xì)描述了 bug 信息及復(fù)現(xiàn)方法:

有了這些上下文信息,就可以避免我們閱讀源碼時(shí)的障礙了。
最后, wiki 頁面可以幫我們了解一些重要的功能設(shè)計(jì)或改動(dòng) 。
拿 Pulsar 來說,如果需要做比較重要的改動(dòng),需要提出一個(gè) PIP 提案(Pulsar Improvement Proposal),也就是一個(gè)專門講解背景信息、設(shè)計(jì)思路的文檔。
而這些 PIP 文檔就收集在 wiki 頁面:

所以在了解某個(gè)功能模塊的設(shè)計(jì)思路時(shí),可以先去 wiki 頁面看看是否有相關(guān)的 PIP 可供參考。
比如 Pulsar 的事務(wù)實(shí)現(xiàn),就有一個(gè)專門的 PIP 詳細(xì)介紹了設(shè)計(jì)思路,結(jié)合 PIP 的思路指引去學(xué)習(xí)源碼就會(huì)容易很多:

我個(gè)人覺得,好的 PIP 結(jié)合源碼,帶我們把一個(gè)功能從討論設(shè)計(jì)做到落地實(shí)現(xiàn),這就是很好的教科書呀,多花精力去研究,肯定會(huì)有所收獲的。
以上就是最常用的有效信息的獲取途徑,如果你在學(xué)習(xí)使用開源項(xiàng)目時(shí)遇到問題,那么可以嘗試上述的方法去尋找答案。
當(dāng)然,熟練掌握進(jìn)行信息檢索的工具進(jìn)行高效檢索也是重要的技能,比如說 IDEA 的各種搜索、GitHub issue/PR 的搜索語法,這些技巧網(wǎng)上可以很容易搜到,我就不贅述了。
二、源碼閱讀技巧
想真正了解一個(gè)項(xiàng)目,看源碼肯定是逃不掉的一環(huán)。閱讀源碼的好處不用多說了,但閱讀源碼肯定會(huì)花費(fèi)大量時(shí)間,而且這個(gè)過程不會(huì)很輕松。
你想嘛,成熟的開源項(xiàng)目經(jīng)過多年的發(fā)展,功能不斷演進(jìn),很多人往里面寫過代碼,恐怕沒人能保證自己完全了解系統(tǒng)的每個(gè)細(xì)節(jié)。我們閱讀源碼,就好比探索一座龐大的城市,很容易迷失在某個(gè)犄角旮旯。
對于這個(gè)問題,我可以分享一些小技巧。
技巧一、不建議看「死代碼」,建議在調(diào)試實(shí)際問題的過程中理解代碼 。
換句話說,不要拿著代碼硬讀,最好是通過動(dòng)態(tài)調(diào)試來研究每個(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è)簡單的場景,看看 producer 是如何創(chuàng)建的,消息是如何發(fā)送并存儲(chǔ)在 Pulsar 中的。
但如果想跟蹤調(diào)試這段代碼,會(huì)遇到一些問題:
第一個(gè)問題是,我們自己的項(xiàng)目是通過 Maven 引入 client 包的,如果進(jìn)入這些包看到的是反編譯的 class 文件,無法直接看到源碼。就算 IDEA 可以直接幫我們下載源碼,但如果我們在從事 client 的開發(fā),需要 master 分支的最新版代碼,這和上傳到 Maven 的源碼還是不一樣。
這個(gè)問題比較容易解決,我們直接從 GitHub 下載源碼,在 client 包里面創(chuàng)建一個(gè) test 文件寫邏輯,這樣就可以調(diào)試最新的 client 代碼了。
第二個(gè)問題比較棘手,我們想調(diào)通整個(gè) Pulsar 發(fā)送消息的流程,那么這里面肯定要涉及 Pulsar client 和 Pulsar broker 的交互,而 broker 是通過命令行啟動(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ù)對不上的問題。
如果出現(xiàn)源碼對不上的情況,可以在 pulsar 項(xiàng)目的根目錄用 maven 重新編譯當(dāng)前的源碼:
$ mvn package -DskipTests -Dlicense.skip=true
編譯好的二進(jìn)制包在distribution/server/target中,我們在新的包中的bin/pulsar腳本添加遠(yuǎn)程 debug 的參數(shù),然后再次啟動(dòng)即可順利調(diào)試。
-
源碼
+關(guān)注
關(guān)注
8文章
685瀏覽量
31317 -
開源項(xiàng)目
+關(guān)注
關(guān)注
0文章
38瀏覽量
7603
發(fā)布評論請先 登錄
Matepad pro12.2 已上市半個(gè)月,但是還沒有在開源網(wǎng)站看到該項(xiàng)目的開源信息,違背開源精神
機(jī)友分享 | 導(dǎo)入機(jī)智云Android開源項(xiàng)目的正確姿勢
下載編譯源碼的要點(diǎn)和搭建源碼閱讀環(huán)境的方法
STM32項(xiàng)目開發(fā)中超級實(shí)用技巧分享
2018 年 2 月份 GitHub 上最熱門的開源項(xiàng)目
基于鴻蒙系統(tǒng)開源項(xiàng)目OpenHarmony源碼靜態(tài)分析
分享一個(gè)超級實(shí)用的源碼閱讀小技巧
矩陣顯示器上的新聞閱讀器開源項(xiàng)目
閱讀開源項(xiàng)目源碼的實(shí)用技巧(下)
Raymond Roussel閱讀機(jī)開源分享
如何去閱讀源碼,我總結(jié)了18條心法
閱讀開源項(xiàng)目源碼的實(shí)用技巧(上)
評論