【轉】如何深入理解開源項目

如何深入理解開源項目

96 
SpeedFirst 已關注
2018.01.04 23:08 字數 2868 閱讀 233評論 2

轉載下羣主大大閱讀源碼的文章,出處:https://www.jianshu.com/p/0e28eaefaded

歡迎大家加入QQ羣交流:317060090

--------------------------------------------------------------------------------------------------

感謝這個時代,我們有了github,有了近乎無窮的開源項目可以看,可以學。

記住,看的目的是學,但是看不等於學

從小代碼集看起

對於一個新手,是絕對不適合一上來就追求spring,web容器,數據庫這種級別的代碼。萬事開頭總是要從簡單的來。如果沒有太多閱讀開源代碼經驗的話,請從一個代碼量在千行級別或更小的repository開始吧。在閱讀代碼中,也慢慢留意一些約定,如

  • 代碼的文檔一般寫哪裏了(README.md ? 或者 docs目錄?)
  • 代碼的起名字和目錄組織大概遵循什麼規範
  • 代碼如何配置
  • 代碼如何build

這些將幫你構建一個初步的可以深入代碼的路徑,併爲進一步深入理解代碼打基礎。

這一點不得不誇一下javascript的npm。npm有相當多的很小的好代碼。老牌web框架express的代碼數才4000多行。一些小工具比如隨機字符串產生器、分佈式ID產生器只有數十行。非常適合入門學習。

聚焦

有多少人是抱着一顆不切實際的預期去看代碼的?有人說,我Java想提升一下去看看Spring吧;有人說我Web沒有太理解,去看看Tomcat吧。結果可想而知。正像問問題時不應該問過於寬泛的問題,看任何代碼都不應該抱有看一遍全都看懂的期望——因爲就連代碼作者自己都做不到。

寫代碼時往往都會做一些抽象,把某個特定問題拆解。比如分層、比如抽象爲一個class代表一個實際的概念等等。每個抽象都可能解決一個具體的問題。看代碼也是如此,一定要先聚焦,把看代碼的scope限制住,不要貪多。

比如當看Java Collections代碼,可能是希望學習其數據結構的實現方式——到底鏈表、樹、跳錶等是怎麼實現的,內存中一個個object是如何關聯起來的,如何被快速訪問的;又或者是特定算法的實現(比如Collections.sort用的是什麼排序算法)。此時,其他的部分就不要太過於在意,直接忽略那些抽象隱藏起來的地方,以及不相關的細節。

對於更復雜的服務就更要聚焦。比如Jetty解決了相當多問題,比如:如何啓動、如何找到Java Runtim、如何加載配置文件、如何load核心class、如何打log如何處理IO、如何解析HTTP協議的數據、如何將數據轉換爲Servlet標準的處理、如何管理集羣…… 並且,這些代碼裏還會夾雜着一些設計模式的層,比如XXXXFactory,XXXXAdaptor…… 如此複雜的代碼,即使是很有經驗的人也不可能兼顧着在短時間內全看懂。所以,每次看代碼之前,務必先確定一個要學習的目的。如果代碼量很龐大,就可以安排一個學習計劃,每次聚焦於一個目的。

對於像Java這樣的面嚮對象語言,優秀的設計往往都是基於一組代表概念的類生成的對象的相互交互。學習代碼時,優先去看類名,組織起高層的全局感非常重要。比如,如果希望學習一個“駕駛模擬”系統的代碼中有關“如何駕駛”的部分,一定會找到代表操作人、油門和方向盤概念的類。然後去觀察他們是如何互動的。此時絕對不該扯上變速箱、發動機和傳動軸。

作爲像C這樣的語言,其源碼大致是面向過程的,即分多個步驟做一件事,每個步驟再細分爲更多子步驟。例如,nginx分配一段內存來存儲一個http請求頭就大概包括

  1. 利用一個工具函數分配指定大小的內存
  2. 從socket中將數據讀出來,並填充到分配的內存上

而第一步可以進一步細化爲:從一個內存池把一段內存借出來,如果沒有可用內存了就得找操作系統要,要到了內存可能還需要填充爲零……等等子步驟。這時預先畫畫流程圖對理解代碼會非常有幫助。

總之,如果你被龐大的源碼打敗,大概率不是因爲你笨,而是因爲你過於貪心急躁了。

請先看文檔

任何代碼,總得有個出現的動機和大致的工作原理。好的代碼一般會在文檔裏(一般是README.md)裏講得比較明白。比如Redis的源代碼的README.md就給出了非常概要性的信息,見https://github.com/antirez/redis。如果還需要對某個特定的主題的解釋,Redis官網也提供了大量的文字來說明,比如

這些文檔能非常好的指導閱讀相關的源代碼。

更復雜的開源系統往往都有對應的書籍來解釋其內部工作原理。比如,《MySQL技術內幕:InnoDB存儲引擎》是很好的指引如何理解InnoDB源代碼的書,大名幾乎每個做業務的同學都會接觸到;當年侯捷先生的《深入淺出MFC》非常細緻的剖析了MFC內部的C++是怎麼把本來C++運行時做不到的事情通過一些歪招給搞定的;《C專家編程》(又稱魚書),用很多小例子來解釋比如一個複雜的函數指針的是怎麼被parse的,變量是如何被保存和傳遞的。等等等等。

如果你閱讀的是著名系統的源代碼,請儘量先從文檔/書籍入手找到切入點,往往能事半功倍。對於可讀性,給人寫的東西總是好過給機器寫的東西啊。

關注資源的生命週期

有生產意義的系統一般總是會有一些核心的資源需要管理,而這些資源的生命週期的維護往往是這類系統代碼的核心。

比如

  • 對於spring-core來講,其核心資源是“Bean”。一個Bean被創建、初始化、被使用、被解構,是整套代碼的核心;
  • 對於spring-webmvc,其核心資源是“HTTP 請求”。一個http請求從被收到開始、其數據被注入到請求handler,其返回的數據結構被設定,是整套代碼的核心;
  • 對於一個池 (比如commons-pool,thread-pool),其核心資源是池中的Object。Object從創建,被借出,被使用,被歸還,到最後被銷燬,是整套代碼的核心

數據庫系統、隊列系統、web系統、一些業務系統(比如做活動、發紅包)、安全系統等等,都有這樣的資源的存在。把握住核心資源的生命週期就能掐到代碼的命門。

找一個好工具

很多年前我們做C開發時都喜歡用一款叫做"Source Insight"的軟件來學習代碼。他可以開很多窗口,在不同的函數間跳來跳去,還可以做書籤方便定位。

如今,基本上是個IDE都會有這些功能,就連沒有類型的js也能很方便的在VS Code中做各種符號跳轉和多串口切換。

代碼嘛,瞭解其執行順序,而非其寫作順序更有利於學習。

建立調試環境

如果對於某些系統需要特別細緻的理解,就需要把代碼跑起來。通過打斷點,輸出log驗證等方式印證自己的想法。

對於js,python這類會相當簡單,因爲npm/pip等工具解決了很多依賴問題,而且無需編譯,直接啓動-修改-重啓-修改-……即可不斷的嘗試。jvm類的系統只要能滿足mvn install或者gradle build這樣的通用編譯約定,或者通過IDE直接加載,也能相對容易的把系統跑起來。

對於C/C++的系統就要麻煩許多,需要自己建立一個虛擬機,然後自行安裝必要的包。對於它們,遠程GDB之類的技術是非常必要的。

對於前端代碼,使用jsfiddle配合chrome開發工具這樣的工具可讓你快速的構建一段js+html+css代碼的片段,並且實時的看到效果。

值得提一句的是,有些系統可能涉及到比較複雜的多進程/線程併發執行,對調試學習非常不利。此時需要優先尋找將系統退化到單線程/進程的運行方式(畢竟代碼作者自己也得靠這個模式調試不是:)。一個典型的例子是nginx通常會有1個master進程+多個worker進程併發運行,但在開發時只要配置中設定:

daemon off;
worker_processer 1;

即可使其退化爲單進程運行時模式。

看代碼很累,要堅持

就像跑步能跑多遠,跑多久,都是要靠自己。閱讀代碼的確能極大地提高個人能力。但是能走多遠要靠毅力堅持。而堅持的大敵就是過大的挫折感。上面提到的種種方法——從簡單的入手、聚焦、有計劃、找好工具,說白了就是儘量減少挫敗感,最大限度的提高“正反饋”。但是即便如此,不可否認的是,對於大多數人,看代碼是一項非常枯燥的過程。這時你的恆心,你的毅力,你的“熬”的勁頭決定了你能走多遠。畢竟,想成爲高級程序員並不是輕輕鬆鬆的事情。

橫下一條心來,挑戰自我吧。


最後附上部分我曾經看過的高質量開源代碼:

  • nginx
  • MFC
  • Redis
  • Lucene
  • Jetty
  • Kafka
  • Thrift
  • Java Executors
  • Java Collections
  • Java Concurrecy
  • express
  • koa
發佈了14 篇原創文章 · 獲贊 28 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章