如何高效地學習開源項目

你好,我是華仔。今天這期「特別放送」,我想和你聊聊如何高效地學習開源項目,一方面澄清開源項目學習過程當中的幾個誤區,另外一方面談談我本身具體實踐時的一套方法論。html

得益於開源運動的蓬勃發展,衆多技術頂尖的公司、團隊或者我的經過開源的方式向技術社區貢獻了許多優秀的開源項目,一方面大大促進了總體技術的發展,另外一方面大大減輕了中小公司和團隊在技術方面的投入壓力,讓團隊可以更加聚焦於業務。前端

開源項目對團隊和業務有很大好處,但對於技術人員來講,若是隻是簡單的採起「拿來主義」,那就變成一個陷阱:看似很快的用開源項目實現了需求,但本身的技術水平並無什麼提高;甚至可能出現看起來用了不少開源項目,知道不少項目名稱,但技術水平止步不前的窘境。redis

所以,對於開源項目,不能簡單的採起「拿來主義」,而要比較深刻的去學習開源項目,作到「知其然,知其因此然」,一方面是爲了更好地應用這些開源項目,另外一方面也是爲了經過學習優秀的開源項目來提高本身的能力。算法

不少技術同窗確實也想深刻學習一些業界成熟和優秀的開源項目,例如 Nginx、Redis、Netty 等,可是在具體實踐的時候,經常由於一些不正確的觀點而誤入歧途,例如:數據庫

  • 只有開發這些開源項目的人才能真正理解,我無法參與這個項目開發,所以我很難深刻理解。編程

  • 個人項目沒有用 Redis,不用的話很難深刻理解。服務器

  • 數據結構和算法很重要,因此我只要研究其數據結構和算法就夠了,例如 Nginx 用的紅黑樹。網絡

  • 「Talk is cheap, show me the code」,一頭扎進源碼逐行閱讀。數據結構

這些觀點要麼讓本身望而生畏從而輕易放棄,要麼讓本身浪費大量時間而沒有多大收穫。那究竟要怎樣作纔是正確的呢?下面我結合本身的經驗談談我對如何學習開源項目的見解。多線程

  • 首先,須要樹立正確的觀念:無論你是什麼身份,均可以從開源項目中學到不少東西

例如,要理解 Redis 的網絡模型,咱們不須要成爲 Redis 的開發者,也不須要必定要用到 Redis,只要具有必定的網絡編程基礎,再經過閱讀 Redis 的源碼,均可以學習 Redis 這種單進程的 Reactor 模型。

  • 其次,不要只盯着數據結構和算法,事實上這兩點在學習開源項目的時候並無那麼重要。

例如,Nginx 使用紅黑樹來管理定時器,對於絕大部分人來講,只要知道這點就夠了,並不須要去研究 Nginx 實現紅黑樹的源碼是如何寫的,除非你須要修改這部分,但我認爲極少人會有這個需求。

  • 第三,採起「自頂向下」的學習方法,源碼不是第一步,而是最後一步

不要一上來就去看源碼,而是要基本掌握了功能、原理、關鍵設計以後再去看源碼,看源碼的主要目的是爲了學習其代碼的寫做方式,以及關鍵技術的實現。

例如,Redis 的 RDB 持久化模式「會將當前內存中的數據庫快照保存到磁盤文件中」,那這裏所謂的「數據庫快照」究竟是怎麼作的呢?在 Linux 平臺上其實就是 fork 一個子進程來保存就能夠了;那爲什麼 fork 子進程就生成了數據庫快照了呢?這又和 Linux 的父子進程機制以及 copy-on-write 技術相關了。

經過這種方式,既可以快速掌握系統設計的關鍵點(Redis 的 RDB 模式),又可以掌握具體的編程技巧(內存快照)。

接下來我詳細談談「自頂向下」的學習方法和步驟。

第一步:安裝

不少人看到「安裝」這個步驟均可能會以爲有點不覺得然:「不就是對照手冊執行一下命令麼,沒什麼技術含量,用的時候裝一下就能夠了」。事實上,安裝步驟遠遠不止這麼簡單,經過具體的安裝過程,你能夠獲取到以下一些關鍵信息:

  • 這個系統的依賴組件,而依賴的組件是系統設計和實現的基礎

以 Nginx 爲例,源碼安裝 Nginx 依賴的庫有 pcre、pcre-devel、openssl、openssl-devel、zlib,光從名字上看都可以瞭解一些信息,例如 openssl 可能和 https 有關,zlib 可能和壓縮有關。

再以 Memcache 爲例,最大的依賴就是 libevent,而根據 libevent 是一個高性能的網絡庫,咱們就能大概推測 Memcache 的網絡實現應該是 Reactor 模型的。

  • 安裝目錄也可以提供一些使用和運行的基本信息

例如,Nginx 安裝完成後,目錄以下:



這個目錄提供的信息有:conf 是存放配置文件的,logs 是存放日誌的,sbin 是運行程序,可是 html 是什麼呢?這個疑問會促使你繼續去研究和學習。

再來看看 Redis,安裝完成後,目錄下只有一個 bin 目錄,具體以下:

我相信大部分人看到這目錄都會感到有點驚訝:這也太簡單了吧,尤爲是與 Nginx 相比!所以也會天然而然的有一些疑問,例如 Redis 如何配置?Redis 日誌保存在哪裏?這些疑問一樣會促使你繼續去研究和學習,帶着問題去學習效率是最高的

  • 系統提供了哪些工具方便咱們使用

一樣以 Redis 爲例,你能夠看到 redis-benchmark、redis-check-aof 等程序,從名字可以大概猜出這些工具的基本使用場景,而這些工具在後面故障定位和處理、性能測試等場景可能很是方便。

第二步:運行

安裝完成後,咱們須要真正將系統運行起來,運行系統的時候有兩個地方要特別關注:命令行和配置文件,它們主要提供了兩個很是關鍵的信息:系統具有哪些能力和系統將會如何運行。這些信息是咱們窺視系統內部運行機制和原理的一扇窗口。

例如,下面是 Memcache 的啓動參數一部分:

經過這幾個啓動參數,你能夠獲取以下一些信息:

  • Memcache 支持 UNIX socket 通訊和 TCP 通訊。

  • Memcache 能夠指定內存大小。

  • lock memory 看起來和內存有關,但具體是什麼意思?配置和不配置有什麼區別麼?

一般狀況下,若是咱們將每一個命令行參數和配置項的做用和原理都所有掌握清楚了的話,基本上對系統已經很熟悉了。個人一個習慣是無論三七二十一,先把全部的配置項所有研究一遍,包括配置項的原理、做用、影響,而且嘗試去修改配置項而後看看系統會有什麼變化。例如,將 Memcache 的「--conn-limit」改成 1 後,查看多個鏈接請求時 Memecache 會返回什麼錯誤、記錄什麼日誌等。

第三步:原理研究

完成前兩個步驟後,咱們對系統已經有了初步的感受和理解,此時能夠更進一步去研究其原理。其實在研究命令行和配置項的時候已經涉及一部分原理了,可是還不繫統,所以咱們要專門針對原理進行系統性的研究。這裏的關鍵就是「系統性」三個字,怎麼纔算系統性呢?主要體如今以下幾個方面:

  • 關鍵特性的基本實現原理

每一個流行的開源項目之因此可以受到大衆的歡迎,確定是有一些賣點的,常見的有高性能、高可用、可擴展等特性,那到底這些項目是如何作到其所宣稱的那麼牛的呢?這些牛 X 的技術實現就是咱們要學習的地方。

例如,Memcache 的高性能具體是怎麼作到的呢?首先是基於 libevent 實現了高性能的網絡模型,其次是內存管理 Slab Allocator 機制。爲了完全理解 Memcache 的高性能網絡模型,咱們須要掌握不少知識:多路複用、Linux epoll、Reactor 模型、多線程等,經過研究 Memcache 的高性能網絡模型,咱們可以學習一個具體的項目中如何將這些東西所有串起來實現了高性能。

再以 React 爲例,Virtual DOM 的實現原理是什麼、爲什麼要實現 Virtual DOM、React 是如何構建 Virtual DOM 樹、Virtual DOM 與 DOM 什麼關係等,經過研究學習 Virtual DOM,即便不使用 React,咱們也可以學習如何寫出高性能的前端的代碼。

  • 優缺點對比分析

這是我想特別強調的一點,只有清楚掌握技術方案的優缺點後纔算真正的掌握這門技術,也只有掌握了技術方案的優缺點後才能在架構設計的時候作出合理的選擇

優缺點主要經過對比來分析,即:咱們將兩個相似的系統進行對比,看看它們的實現差別,以及不一樣的實現優缺點都是什麼。

典型的對比有 Memcache 和 Redis,例如(僅舉例說明,實際上對比的點不少),Memcache 用多線程,Redis 用單進程,各有什麼優缺點?Memcache 和 Redis 的集羣方式,各有什麼優缺點?

即便是 Redis 自身,咱們也能夠對比 RDB 和 AOF 兩種模式的優缺點。

在你瞭解了什麼是「系統性」後,我來介紹一下原理研究的手段,主要有三種:

  • 通讀項目的設計文檔:例如 Kafka 的設計文檔,基本涵蓋了消息隊列設計的關鍵決策部分;Disruptor 的設計白皮書,詳細的闡述了 Java 單機高性能的設計技巧。

  • 閱讀網上已有的分析文檔:一般狀況下比較熱門的開源項目,都已經有很是多的分析文檔了,咱們能夠站在前人的基礎上,避免大量的重複投入。但須要注意的是,因爲經驗、水平、關注點等差別,不一樣的人分析的結論可能有差別,甚至有的是錯誤的,所以不能徹底參照。一個比較好的方式就是多方對照,也就是說看不少篇分析文檔,比較它們的內容共同點和差別點。

  • Demo 驗證:若是有些技術點難以查到資料,本身又不肯定,則能夠真正去寫 Demo 進行驗證,經過打印一些日誌或者調試,能清晰的理解具體的細節。例如,寫一個簡單的分配內存程序,而後經過日誌和命令行(jmap、jstat、jstack 等)來查看 Java 虛擬機垃圾回收時的具體表現。

第四步:測試

一般狀況下,若是你真的準備在實際項目中使用某個開源項目的話,必須進行測試。有的同窗可能會說,網上的分析和測試文檔不少,直接找一篇看就能夠了?若是隻是本身學習和研究,這樣作是能夠的,由於構建完整的測試用例既須要耗費較多時間,又須要較多機器資源,若是每一個項目都這麼作的話,投入成本有點大;但若是是要在實踐項目中使用,必須本身進行測試,由於網上搜的測試結果,不必定與本身的業務場景很契合,若是簡單參考別人的測試結果,極可能會得出錯誤的結論。例如,開源系統的版本不一樣,測試結果可能差別較大。一樣是 K-V 存儲,別人測試的 value 是 128 字節,而你的場景 value 都達到了 128k 字節,二者的測試結果也差別很大,不能簡單照搬。

測試階段須要特別強調的一點就是:測試必定要在原理研究以後作,不能安裝完成立馬就測試!緣由在於若是對系統不熟悉,極可能出現命令行、配置參數沒用對,或者運行模式選擇不對,致使沒有根據業務的特色搭建正確的環境、沒有設計合理的測試用例,從而使得最終的測試結果得出了錯誤結論,誤導了設計決策。曾經有團隊安裝完成 MySQL 5.1 後就進行性能測試,測試結果出來讓人大跌眼鏡,通過定位才發現 innodb_buffer_pool_size 使用的是默認值 8M。

第五步:源碼研究

源碼研究的主要目的是學習原理背後的具體編碼如何實現,經過學習這些技巧來提高咱們本身的技術能力。例如 Redis 的 RDB 快照、Nginx 的多 Reactor 模型、Disruptor 如何使用 volatile 以及 CAS 來作無鎖設計、Netty 的 Zero-Copy 等,這些技巧都很精巧,掌握後可以大大提高本身的編碼能力。

一般狀況下,不建議通讀全部源碼,由於想掌握每行代碼的含義和做用仍是很是耗費時間的,尤爲是 MySQL、Nginx 這種規模的項目,即便是他們的開發人員,都不必定每一個人都掌握了全部代碼。帶着明確目的去研究源碼,作到有的放矢,才能事半功倍,這也是源碼研究要放在最後的緣由。

對於一些基礎庫,除了閱讀源碼外,還能夠本身寫個 Demo 調用基礎庫完成一些簡單的功能,而後經過調試來看具體的調用棧,經過調用棧來理解基礎庫的處理邏輯和過程,這比單純看代碼去理解邏輯要高效一些。例如,下面是 Netty 4.1 版本的 telnet 服務器樣例調試的堆棧,經過堆棧咱們能夠看到完整的調用棧:

時間分配

前面介紹的「自頂向下」5 個步驟,完整執行下來須要花費較長時間,而時間又是大部分技術人員比較稀缺的資源。不少人在學習技術的時候都會反饋說時間不夠,版本進度很緊,很難有大量的時間進行學習,但若是不學習感受本身又很難提高?面對這種兩難問題,具體該如何作呢?

一般狀況下,以上 5 個步驟的前 3 個步驟,無論是已經成爲架構師的技術人員,仍是立志成爲架構師的技術人員,在研究開源項目的時候都必不可少;第四步能夠在準備採用開源項目的時候才實施,第五步能夠根據你的時間來進行靈活安排。這裏的「靈活安排」不是說省略不去作,而是在本身有必定時間和精力的時候作,由於只有這樣才能真正理解和學到具體的技術。

若是感受本身時間和精力不夠,與其走馬觀花每一個開源項目都去簡單瞭解一下,還不如集中精力將一個開源項目研究通透,就算是每一個季度只學習一個開源項目,積累幾年後這個數量也是很客觀的;並且一旦你將一個項目研究透之後,再去研究其餘相似項目,你會發現本身學習的很是快,由於共性的部分你已經都掌握了,只須要掌握新項目差別的部分便可。

相關文章
相關標籤/搜索