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