「我能熟練使用這個框架/軟件/技術就好了, 爲何要看源碼?」java
「平時不用看源碼, 看源碼太費時間,還容易忘記,工做中出現問題再針對性地閱讀,效率更高。」程序員
「爲了面試才須要看源碼!」面試
。。。。。。算法
若是你也有相似的疑問,不妨接着往下看編程
一、爲何要閱讀源碼?
1.1 在通用型基礎技術中提升技術能力
在 JAVA 領域中包含 JAVA 集合、Java併發(JUC)等, 它們是項目中使用的高頻技術,在各類複雜的場景中選用合適的數據結構、線程併發模型,合理控制鎖粒度等都能顯著提升應用程序的可用性、健壯性,很是容易凸顯出本身的技術實力,更容易受到領導的承認,助力職場。設計模式
固然經過閱讀源碼並非知曉原理的惟一方法,但做爲一個名程序員、直面代碼,親自感覺代碼的魅力或許會顯得的更加直接。數組
1.2 在重點領域打造本身的亮點
我所在公司使用了 Dubbo、RocketMQ,我也有幸參與到這些技術棧的運用與運維,積累了豐富的使用經驗,爲了突出在這兩個領域的優點,我詳細閱讀了它們的源碼,在CSDN和公衆號等知識分享平臺發佈了大量的技術文章,成體系的剖析其實現原理、架構設計理念,理論與實戰相結合,讓我成爲在Dubbo、RocketMQ領域當仁不讓的技術專家,團隊中的核心骨幹。數據結構
同時因爲文章是成體系的,被出版社相中,邀請出書,《RocketMQ技術內幕》一書應運而生,從而成爲我職業技能列表中很是亮眼的名片,造成公認的技術影響力,具有必定的「品牌溢價」能力。架構
固然, 也能夠等到出了問題再看源碼,「投入產出比」更高,但這是個被動過程,若是生產環境因爲併發不高,那可能一年、兩年你都遇不到真正的問題,經驗積累很是慢, 工做了四、5年,有可能還比不過工做二、3年的人。併發
因此若是是你想快速打造亮點的話,仍是須要主動閱讀源碼,成體系掌握其設計理念、實現原理。
1.3 從優秀的源碼中學習設計和編碼
學習編程的過程其實就是一個模仿的過程, 優秀的源碼都是大師級做品,極具養分,能夠看到大師們是如何抽象接口的,如何應用SOLID原則的,還有不少很是有用的編程技巧。
例如JUnit是從模式開始構建系統, 其中你能夠看到大量的設計模式的應用,這些都是活生生的案例,比干巴巴地看那些設計模式理論,或者簡單的例子不知道好到哪裏去了。
二、如何閱讀源碼
我根據多年的閱讀經驗,整理了這麼一套方法:
- 瞭解這款軟件的使用場景、以及架構設計中將承擔的責任。
- 尋找官方文檔,從總體上把握這款軟件的設計理念。
- 搭建本身的開發調試環境,運行官方提供Demo示例,爲後續深刻研究打下基礎。
- 先主幹流程再分支流程,注意切割,逐個擊破。
接下來分享一下我在閱讀 RocketMQ 源碼時的一些經歷,儘可能讓上述理論具備畫面感。
2.1 瞭解 RocketMQ的應用場景
MQ的使用場景是比較清晰的,它的兩大基本職責是解耦與削峯填谷。
舉一個最簡單的場景:新用戶註冊送積分、送優惠券場景,其原始架構設計一般以下:
能夠看出用戶註冊和發優惠券,送積分是緊耦合的, 隨着業務不斷髮展,活動部門提出在春節期間用戶註冊不送積分,發優惠券,而是贈送一個新春禮包,若是基於上述架構的話,須要去改動用戶註冊的主流程,違背了設計模式中的對修改關閉、對擴展開放的設計理念。
MQ的出現,能夠很好地解決上面的問題:
經過引入MQ,用戶註冊主流程只須要完成註冊邏輯,並向MQ發送一條消息,而後活動模塊(送積分、送優惠券、送禮包)只須要訂閱MQ中的消息,進行對應的處理。
這樣消息註冊主流程會很是的簡單,無論活動種類如何變化,註冊流程無需更改,這樣就實現瞭解耦。
2.2 通讀官方文檔,從全局把握其設計理念
瞭解使用場景之後,接下咱們能夠去查閱官方文檔,主要包括用戶設計文檔(架構設計),用戶使用手冊等,從全局瞭解其設計理念。
經過通讀官方文檔,不只能夠得出MQ的總體脈絡(例如NameServer路由發現、消息發送、消息存儲、消息消費、消息過濾),也能對順序消費,零拷貝、同步刷盤、異步刷盤等「高端大氣上檔次」的高級特性產生興趣與好奇,驅動咱們去閱讀其源碼,探究其實現細節,使得咱們在閱讀源碼中進行必定的自我思考成爲了可能。
2.3 搭建開發調試環境
不一樣的系統搭建方式也不一樣,我這裏有一篇手把手教你安裝RocketMQ與IDEA Debug環境搭建。
2.4 先主幹,再分支
在搭建好本地開發環境後,切忌直接用Debug去跟蹤消息發送的總體流程,由於這個流程實在是太長了,從比較粗粒度來看其流程以下圖所示:
若是你們想一次性將上述流程的源碼所有看一遍,幾乎是不可能的。 由於消息發送高可用設計、消息存儲、刷盤、同步等機制,每一個點詳細展開的工做都是海量的,咱們沒有這麼多連續的時間,因此適當的拆分很是有必要。
通過這樣一分解,就能專一理解其某一塊的設計原理,所須要的連續時間也能大大減小,一口一口「吃」,最終完成整個體系的理解。
這還帶來了一個額外的好處:分批次輸出多篇文章,提升了文章產出,提升了成就感。
三、閱讀源碼很容易放棄,怎麼辦?
閱讀源碼是枯燥的,一我的孤軍奮戰很容易放棄,尤爲是遇到問題的時候, 如何才能堅持下去,把它讀完呢?
個人答案是堅持,但容許短暫的休整、停留,而後反覆發起衝刺,拼搶,直到攻克這個「山頭」。
由於一旦放棄就將前功盡棄,一旦突破,自身能力能獲得一個質的飛躍。
我在閱讀Netty源碼時就遇到了難題,當時剛剛寫完Netty的內存泄露檢測,準備開始研究內存分配機制, 這一起很是抽象,涉及的數據結構複雜,須要掌握二叉樹與數組之間如何映射、牽扯到大量的位運算等等,讓我在探究其原理時步履維艱,怎麼辦呢?放棄?
當一週、兩週都沒法取得突破時,人很容易想到:這樣持續投入時間,又沒有回報, 是否是在浪費時間?
其實我想過放棄,但轉念一想:放棄後,我會作什麼呢?玩遊戲?看電視?
既然是玩遊戲、看電視,不更是浪費時間嗎?想清楚這一層後,繼續攻關,繼續突破就成了惟一的選擇。
固然,遇到難題了,偶爾放鬆一兩天,從新調整狀態也是很是必要的。短暫的休整可讓咱們變得不那麼焦躁,有利於咱們從新整理思路,從新發起衝刺。
當時遇到難題時,採起哪些措施有利於咱們攻克難題呢?
一、向「度娘」求救
在閱讀源碼時能夠將看不懂的代碼直接COPY一小段到百度上去搜索,可能會有大牛已經對這些代碼作過解讀,能起到指點做用。
當時通過個人搜索,發現Netty的內存分配算法並非獨創,而是對 jemalloc 算法的實現,經過查閱相關的技術文檔,能夠從總體上理解Netty的內存分配算法。
二、Debug
Netty追求極致的性能,採用了大量的位運算,因爲平時工做中不多會使用位運算,看起來比較吃力,爲了彌補這方面的不足,使用DEBUG模式,結合運行時數據,去理解位運算,更容易開竅。
通過上面的努力,花了10天時間,Netty的內存分配機制慢慢被我解開,通過分解,我寫了一系列文章來描述Netty內存分配:
邁過了這道坎,後面的源碼閱讀效率變得很是高效。
經過攻克一個個難題,最終從量變引起質變,逐漸造成一條本身的源碼閱讀理論,後續的源碼分析RocketMQ、Dubbo、ElasticJob、Sentinel、Kafka等專欄就變得很是簡單了。
四、源碼閱讀的三層境界
4.1 初級:記流水帳
我初期的源碼閱讀文章基本上是記流水帳,例如對源碼同樣一行加註釋,只關注底層實現細節,但並未造成更高層次認知,對其設計理念沒有提煉與深度領悟。
4.2 中級:能提問、思考、提煉
隨着技術類文章的持續分享, 我認識了不少大牛,發現和他們交流的時候,發現他們一開始並不會說細節,而是講設計理念。
這就要求咱們在閱讀源碼的時候多思考,並反問本身若是本身實現的話該如何着手,如何設計,帶着疑問去研究源碼。經過對比,思考,會對其背後的理念有了更深入的理解。
4.3 高級:思考、質疑、驗證
無論是哪一個開源框架,都會存在BUG或者實現並不合理的地方,若是你們在閱讀源碼的時候可以深刻思考, 合理質疑,並能經過驗證證實本身的觀點,而後與官方取得聯繫,交流,共同促進社區的發展,說明咱們的能力、思考獲得了極大的提高。
思考與質疑是源碼閱讀的一個昇華,好比我在看Sentinel 熔斷時對其提出的質疑。
一個「用戶信息查找」服務,被部署到了三個機器上。
其中192.168.1.3這個機器有一段時間負載太高,響應時間過長,發往它的請求有30%都失敗了。
而30%偏偏是Sentinel設置的熔斷錯誤率, 因而Sentinel認爲整個「用戶信息查找」服務不可用了,熔斷了。
這明顯是不合理的,由於192.168.1.4和192.168.1.5這兩個機器還活着呢!
本質上這是由於熔斷錯誤率被定義到了服務級別 :服務 -> 熔斷錯誤率
在對這個問題進行思考的時候,我認爲在配置熔斷規則的時候,須要細化,應該把熔斷錯誤率定義到資源組級別:[服務 , 機器] -> 熔斷錯誤率
這樣,當一個機器上的服務提供者被熔斷後,不影響其餘機器,保證了真正的高可用。
經過與官方聯繫,我這個想法獲得了其做者的確定,反向促進了社區的發展。
推薦閱讀
15年教學經驗的Java架構師用140個實戰案例完美演示微服務+豐富架構圖,輕鬆掌握微架構設計與開發
看完三件事❤️
若是你以爲這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
點贊,轉發,有大家的 『點贊和評論』,纔是我創造的動力。
關注公衆號 『 Java鬥帝 』,不按期分享原創知識。
同時能夠期待後續文章ing🚀