利用開原始碼和讀相關論文來提高寫程式碼能力

NO IMAGE

640?wx_fmt=gif&wxfrom=5&wx_lazy=1

本文來自作者 李峰 在 GitChat 上分享 「利用開原始碼和讀相關論文來提高寫程式碼能力」,「閱讀原文」檢視交流實錄。

「文末高能」

編輯 | 哈比

越來越多的軟體開源以及搜尋引擎帶來的便利,大大提高了工程開發效率。但是相對於程式設計師來說,這種效率的提高卻給自身成長帶來了很多困惑:

  • 對於大部分普通工程師來說,每天的工作內容只不過是在原有程式碼基礎之上修修改改,用著框架做著重複單調的 CURD 工作,寫著不痛不癢的業務程式碼,反過頭來看自己所掌握的技能,覺的什麼都會一點,可是都不精通,開始變的焦慮不自信;

  • 目前需獨立開發一個全新系統,卻毫無頭緒,不知道怎麼做架構設計;

  • 有些同學想通過學習開原始碼來提高自己,可是一看到程式碼量這麼龐大,卻無從下手或者望而卻步。

那麼在平常的工作中如何提高自己的程式碼和架構能力呢?本文以相關程式碼為例子,具體說明如何讀原始碼和論文,並且掌握其中的思想來為我所用,最終可以做到寫程式碼有理可尋,有據可依。

文章的大致結構:第一部分,開原始碼,具體從熟悉、閱讀、應用實踐原始碼三個階段敘述;第二部分,論文,具體從論文閱讀和論文應用實踐兩個方面論述;最後一部分總結。

一、開原始碼

原始碼被看做是學習的寶藏,從中可以學到很多先進思想和技術。很多同學都曾想通過讀原始碼提高自己,可是面對這龐然大物,有的同學選擇一頭扎進去開始從頭開始學,最後收穫甚微,只能放棄,而有些同學則心生畏懼,望而卻步,最終一無所獲。

其實學習任何一新技能和知識,都需要經過一個過程,從熟悉,到深入,最後到實踐為我所用,然後再繼續熟悉,以此迴圈往復,整個過程需要付出很大的耐心和精力。

同樣,閱讀開原始碼亦是如此,在開始這項工作前,一定要克服心理魔障,從戰略上藐視對方,“哼,讀原始碼並沒什麼了不起的 “,戰術上一定要重視。

做好充分的心理準備後,開始關於原始碼的戰術分析。

1. 熟悉

學習任何新東西都有一個熟悉的過程,切忌一開始直接下載原始碼從頭開始啃,這種方法只會事倍功半,而且很容易產生挫敗感直至放棄。學習需要有一個循序漸進的過程。

在開始正式閱讀原始碼之前,首先要經歷熟悉的三個階段。

1.1 閱讀文件,熟悉基本概念

在遇到一個新的軟體,一定要先閱讀文件瞭解其解決什麼問題,涉及到的核心概念,每個模組的具體職責。如果不看文件直接開啟原始碼,會發生什麼事呢?

開啟原始碼的瞬間,各種類的定義迎面而來,spout,tuple,stream,nimbus,一看是不是想死的心都有了。所以,事情說三遍,基本概念一定要弄清楚,概念越清楚,系統瞭解的越透徹。

常用文件 Read List:

  • 官方文件:常用的開原始碼都有其公開的網站。

  • wiki:對應 github 倉庫的 wiki 文件。

  • 系統對應的發表論文:比如 Bigtable、Zookeeper、Kafka 等都有論文發表。

這裡以 storm 為例子:

640?wx_fmt=jpeg文件中,storm 提出了一些新的概念,spouts,bolts,詳細閱讀這些名詞背後所表述的含義以及在系統中擔任的職責。

文件內容需要反覆的讀,每次讀都會有不同的新的認識。

1.2 實踐使用

具體實踐活動:

  • 按官方文件部署相關軟體,準備執行環境。

  • 寫一個 hello world 的 demo 執行起來:開發包中一般存在資料夾名包含 starter 和 example 字眼的資料夾,該資料夾下會有許多官方使用例子。

  • 修改 demo 實現自己的業務邏輯。

這個階段主要是模仿官方的例子實現自己的業務邏輯以及熟悉常用的 API。

在實踐的過程中,會遇到各種問題,同時也會看一些相關技術文章,難點一點點攻破,系統的瞭解也會更深入。

1.3 構建資料流圖

看到自己的程式碼可以正確的執行,成就感油然而生。但是,請收起你的放縱,這還遠遠不夠,請繼續下一階段:構建資料流執行圖。

程式的執行其實就是資料的流動運轉,即資料是在模組之間流動互動的,資料從輸入到輸出,經歷了哪些模組,以及模組之間如何協作。

剛開始構建資料流圖不需要很詳細,只要把文件中涉及到的核心概念聯絡起來即可,隨著對系統的深入,回頭反過來繼續充實更多的細節內容。

例如,在學習使用 storm 時,會試著構建這麼一個資料流圖:

640?wx_fmt=png

為什麼要花時間構建這樣一個圖呢?

  • 理解概念,瞭解系統的執行機制;

  • 逼迫自己去思考,比如模組之間如何互動,資料流怎麼流動。腦海裡有越多的問題,看原始碼的動力就越足;

  • 隨著對系統的逐漸瞭解,可以一步步完善,新增一些具體的實現細節,比如 task 如何知道輸出結果應該發往那個 task 呢,其地址是多少呢?

  • 遇到問題時,可以依照該圖猜測問題可能出在哪個模組,迅速找到切入點。

Tips:在分析系統時,可以從資料流動的角度來思考這個問題。

2. 閱讀

通過熟悉階段,腦海裡積累了很多疑惑,已經迫不及待的開始要閱讀程式碼解惑了。切記,閱讀程式碼一定要化整為零,從小模組開始。具體過程則是提出問題,大膽猜想,小心驗證。

閱讀程式碼不是簡單的讀,而是一個思維碰撞的過程:大膽猜想非常重要。

當遇到一個技術問題時,首先一定要先獨立思考,想一下:如果換作自己該如何解決以及實現這個功能,當有了自己的解決思路後,然後再對照原始碼驗證。當發現自己的思路跟作者是一樣的,那種快感,哈哈,慢慢體會。

2.1 準備階段

在開始具體的程式碼閱讀時,還要做些技術儲備。首先我會先看下該軟體所依賴的外部庫,從瞭解這些外部庫開始。為什麼要從這兒開始看呢?

其實軟體開發就像搭積木一樣,由一個個小的模組拼裝而成,只有瞭解了每個小模組的正確使用方式,才能更流暢的剖析如何搭積木的。

這裡以 JAVA 專案為例子,開啟 pom 或者 gradle 檔案檢視,該專案依賴了哪些外部庫。

640?wx_fmt=jpeg

這裡是 storm 依賴的外部庫,裡面用到了 curator(zookeeper 使用)、kryo(序列化)、disruptor(生產者消費者模式庫)、netty(網路)。這裡需要額外花些時間研究下該庫的解決的問題、使用方法和常用 API。

同時,這也是知識儲備的過程,以後遇到類似的問題,可以聯想到對應的庫,為解決問題提供更多的思路。

2.2 切入階段

有了一定的知識儲備,就可以尋找切入點開始在程式碼的汪洋中盡情遨遊。關於切入點的選擇,自己有幾點體會:

(1)從熟悉的基礎依賴庫入手,檢視它在程式碼中是如何使用的。比如基礎庫中對 netty 使用比較熟悉,就直接找程式碼裡對應的 handler 具體實現,pipline 的構成以及 netty 的設定管理等。

從自己熟悉的東西入手,一方面可以克服恐懼,同時通過閱讀程式碼可以知道更多的 api 和 api 使用方法,看完之後經常會有“原來還可以這樣使用”的感嘆。

(2)從使用過程中遇到的 bug 入手,依賴錯誤日誌的上下文來檢視程式碼。

(3)從概念模型中提到的概念模組入手,比如 storm 中提到了 tuple,spout,nimbus,worker 等,直接找到對應的實現類(一般類名和概念名是一樣的,可以直接按名字來搜尋),依照其在系統中的職責檢視具體實現。

(4)從問題出發,檢視相應的技術文章,進一步縮小範圍,檢視相關程式碼。問題可以是開發中遇到的異常,構建資料流圖的困惑,或者一些很普遍的疑問,比如資料的儲存格式,通訊協議,資料處理異常的解決方案等,問題越詳細越好。

(5)從 demo 程式碼開始一步步 debug。

2.3 深入階段

在深入程式碼的閱讀過程中,沒有必要一行一行的閱讀,有些方法可以當作黑盒子,有些 if-else 分支可以直接略過(忽略一些異常條件情形)。最後模仿程式碼自己動手寫一些簡單實現。

2.4 整理階段

通過一定時間的學習,看了一部分程式碼,即使做總結,一方面可以反過來豐富前文提到過的資料流圖,完善一些細節,同時也可以寫一些技術文章條理一下思路。

3. 應用實踐

俗話說,讀書千萬卷,不會吟詩也會騶。同樣的,原始碼看多了,東拼西湊也可以實現所需的功能,其實會抄並且抄的好也是一種能力。

原始碼會在開發中的哪些方面會有幫助呢?

這裡我總結了幾點可操作的具體實踐:

3.1 概念模型和命名

在開發初期,需要根據業務需求抽象出概念模型以及系統設計文件。這一階段也是最難的部分,剛開始根本無從下手。

那現在有沒有想起熟悉階段我們閱讀過的文件和平時積累的素材呢?其實每個優秀的開源軟體都有很好的概念模型和清晰的系統架構,比如在資料處理中任務執行的系統中經常會聽到這些詞,比如 topology,task, job,manager,service dispatcher,DAGscheduler,context 等。

現在要開發設計一個任務提交管理系統,那這個任務和 storm 的拓撲提交以及 hadoop 的任務提交及其生命週期管理是不是同一個領域問題呢?

很多概念是不是就用上了(job,task,JobManager,JobScheduler,worker,executor,cordinator 等)只要看程式碼命名,是不是瞬間覺得高大上了。

640?wx_fmt=jpeg

關於系統架構圖,有沒有想到前面思考過的系統資料流圖,只要稍微調整一下即可,是不是很容易。

3.2 開源基礎庫的使用

在準備階段會瞭解到很多新的開源應用基礎庫。

比如在看 presto: https://github.com/prestodb/presto 分散式 SQL 執行引擎的時候,瞭解到其中一個元件 presto-parser,可以解析 SQL 語句。

正好,現需要開發一個 SQL 語法解析的功能,引用該基礎庫,幾行程式碼直接搞定。如果需要重新造輪子,可以參考其實現。

SqlParser SQL_PARSER = new SqlParser();
Query query = (Query)SQL_PARSER.createStatement(“select * from tableName”);
QueryBody queryBody = (QuerySpecification) query.getQueryBody();

3.3 部分程式碼的 copy

看程式碼的過程中,經常發現這一段程式碼實現很巧妙,可以記錄下來,當實現類似的需求,可以直接 copy 過來,稍微改一下。比如看到 flink 的限流程式碼:https://goo.gl/yHyq4o,構造 netty 通訊協議程式碼:https://goo.gl/jhBQ7K。

在自己設計 SPE 底層通訊協議:https://goo.gl/DSVrVK 的時候,直接 copy 過來做些調整,開發就是這麼 easy。

二、論文

平時的工作中,很少會接觸論文,閱讀論文的時間少之又少。但是論文真是個好東西,內容包含了作者的一些思考和問題闡述,從技術的起源說到目前的狀況等,而這些內容正好幫助構建和豐富自己的知識體系。

1. 閱讀

1.1 論文的選擇

(1)在閱讀開原始碼的時候,有些程式碼註釋會標註其涉及到的論文:

640?wx_fmt=jpeg

想要理解具體演算法和實現,就需要下載下來仔細閱讀。

(2)比較出名的開源系統(kafka,zookeeper,disruptor 等)都會有相對應的論文。對於想學習分散式系統的同學,有網友已經總結了一些好的論文 readlist:https://goo.gl/rjDrQU,有興趣的可以看下。

掃描下方二維碼,閱讀完整原文。

640?wx_fmt=png