“我能熟練使用這個(gè)框架/軟件/技術(shù)就行了, 為什么要看源碼?”
“平時(shí)不用看源碼, 看源碼太費(fèi)時(shí)間,還容易忘記,工作中出現(xiàn)問(wèn)題再針對(duì)性地閱讀,效率更高?!?/p>
“為了面試才需要看源碼!”
。。。。。。
如果你也有類似的疑問(wèn),不妨接著往下看
在 JAVA 領(lǐng)域中包含 JAVA 集合、Java并發(fā)(JUC)等, 它們是項(xiàng)目中使用的高頻技術(shù),在各種復(fù)雜的場(chǎng)景中選用合適的數(shù)據(jù)結(jié)構(gòu)、線程并發(fā)模型,合理控制鎖粒度等都能顯著提高應(yīng)用程序的可用性、健壯性,非常容易凸顯出自己的技術(shù)實(shí)力,更容易受到領(lǐng)導(dǎo)的認(rèn)可,助力職場(chǎng)。
當(dāng)然通過(guò)閱讀源碼并不是知曉原理的唯一方法,但作為一個(gè)名程序員、直面代碼,親自感受代碼的魅力或許會(huì)顯得的更加直接。
我所在公司使用了 Dubbo、RocketMQ,我也有幸參與到這些技術(shù)棧的運(yùn)用與運(yùn)維,積累了豐富的使用經(jīng)驗(yàn),為了突出在這兩個(gè)領(lǐng)域的優(yōu)勢(shì),我詳細(xì)閱讀了它們的源碼,在CSDN和公眾號(hào)等知識(shí)分享平臺(tái)發(fā)布了大量的技術(shù)文章,成體系的剖析其實(shí)現(xiàn)原理、架構(gòu)設(shè)計(jì)理念,理論與實(shí)戰(zhàn)相結(jié)合,讓我成為在Dubbo、RocketMQ領(lǐng)域當(dāng)仁不讓的技術(shù)專家,團(tuán)隊(duì)中的核心骨干。
同時(shí)由于文章是成體系的,被出版社相中,邀請(qǐng)出書,《RocketMQ技術(shù)內(nèi)幕》一書應(yīng)運(yùn)而生,從而成為我職業(yè)技能列表中非常亮眼的名片,形成公認(rèn)的技術(shù)影響力,具備一定的“品牌溢價(jià)”能力。
當(dāng)然, 也可以等到出了問(wèn)題再看源碼,“投入產(chǎn)出比”更高,但這是個(gè)被動(dòng)過(guò)程,如果生產(chǎn)環(huán)境由于并發(fā)不高,那可能一年、兩年你都遇不到真正的問(wèn)題,經(jīng)驗(yàn)積累非常慢, 工作了4、5年,有可能還比不過(guò)工作2、3年的人。
所以如果是你想快速打造亮點(diǎn)的話,還是需要主動(dòng)閱讀源碼,成體系掌握其設(shè)計(jì)理念、實(shí)現(xiàn)原理。
學(xué)習(xí)編程的過(guò)程其實(shí)就是一個(gè)模仿的過(guò)程, 優(yōu)秀的源碼都是大師級(jí)作品,極具營(yíng)養(yǎng),可以看到大師們是如何抽象接口的,如何應(yīng)用SOLID原則的,還有很多非常有用的編程技巧。
例如JUnit是從模式開始構(gòu)建系統(tǒng), 其中你可以看到大量的設(shè)計(jì)模式的應(yīng)用,這些都是活生生的案例,比干巴巴地看那些設(shè)計(jì)模式理論,或者簡(jiǎn)單的例子不知道好到哪里去了。
我根據(jù)多年的閱讀經(jīng)驗(yàn),整理了這么一套方法:
了解這款軟件的使用場(chǎng)景、以及架構(gòu)設(shè)計(jì)中將承擔(dān)的責(zé)任。
尋找官方文檔,從整體上把握這款軟件的設(shè)計(jì)理念。
搭建自己的開發(fā)調(diào)試環(huán)境,運(yùn)行官方提供Demo示例,為后續(xù)深入研究打下基礎(chǔ)。
先主干流程再分支流程,注意切割,逐個(gè)擊破。
接下來(lái)分享一下我在閱讀 RocketMQ 源碼時(shí)的一些經(jīng)歷,盡量讓上述理論具有畫面感。
MQ的使用場(chǎng)景是比較清晰的,它的兩大基本職責(zé)是解耦與削峰填谷。
舉一個(gè)最簡(jiǎn)單的場(chǎng)景:新用戶注冊(cè)送積分、送優(yōu)惠券場(chǎng)景,其原始架構(gòu)設(shè)計(jì)通常如下:
可以看出用戶注冊(cè)和發(fā)優(yōu)惠券,送積分是緊耦合的, 隨著業(yè)務(wù)不斷發(fā)展,活動(dòng)部門提出在春節(jié)期間用戶注冊(cè)不送積分,發(fā)優(yōu)惠券,而是贈(zèng)送一個(gè)新春禮包,如果基于上述架構(gòu)的話,需要去改動(dòng)用戶注冊(cè)的主流程,違背了設(shè)計(jì)模式中的對(duì)修改關(guān)閉、對(duì)擴(kuò)展開放的設(shè)計(jì)理念。MQ的出現(xiàn),可以很好地解決上面的問(wèn)題:
通過(guò)引入MQ,用戶注冊(cè)主流程只需要完成注冊(cè)邏輯,并向MQ發(fā)送一條消息,然后活動(dòng)模塊(送積分、送優(yōu)惠券、送禮包)只需要訂閱MQ中的消息,進(jìn)行對(duì)應(yīng)的處理。
這樣消息注冊(cè)主流程會(huì)非常的簡(jiǎn)單,不管活動(dòng)種類如何變化,注冊(cè)流程無(wú)需更改,這樣就實(shí)現(xiàn)了解耦。
了解使用場(chǎng)景以后,接下我們可以去查閱官方文檔,主要包括用戶設(shè)計(jì)文檔(架構(gòu)設(shè)計(jì)),用戶使用手冊(cè)等,從全局了解其設(shè)計(jì)理念。
通過(guò)通讀官方文檔,不僅可以得出MQ的整體脈絡(luò)(例如NameServer路由發(fā)現(xiàn)、消息發(fā)送、消息存儲(chǔ)、消息消費(fèi)、消息過(guò)濾),也能對(duì)順序消費(fèi),零拷貝、同步刷盤、異步刷盤等“高端大氣上檔次”的高級(jí)特性產(chǎn)生興趣與好奇,驅(qū)動(dòng)我們?nèi)ラ喿x其源碼,探究其實(shí)現(xiàn)細(xì)節(jié),使得我們?cè)陂喿x源碼中進(jìn)行一定的自我思考成為了可能。
不同的系統(tǒng)搭建方式也不同,我這里有一篇手把手教你安裝RocketMQ與IDEA Debug環(huán)境搭建。
在搭建好本地開發(fā)環(huán)境后,切忌直接用Debug去跟蹤消息發(fā)送的整體流程,因?yàn)檫@個(gè)流程實(shí)在是太長(zhǎng)了,從比較粗粒度來(lái)看其流程如下圖所示:
如果大家想一次性將上述流程的源碼全部看一遍,幾乎是不可能的。 因?yàn)?/span>消息發(fā)送高可用設(shè)計(jì)、消息存儲(chǔ)、刷盤、同步等機(jī)制,每個(gè)點(diǎn)詳細(xì)展開的工作都是海量的,我們沒有這么多連續(xù)的時(shí)間,所以適當(dāng)?shù)牟鸱址浅S斜匾?/span>經(jīng)過(guò)這樣一分解,就能專注理解其某一塊的設(shè)計(jì)原理,所需要的連續(xù)時(shí)間也能大大減少,一口一口“吃”,最終完成整個(gè)體系的理解。
這還帶來(lái)了一個(gè)額外的好處:分批次輸出多篇文章,提高了文章產(chǎn)出,提高了成就感。
閱讀源碼是枯燥的,一個(gè)人孤軍奮戰(zhàn)很容易放棄,尤其是遇到問(wèn)題的時(shí)候, 如何才能堅(jiān)持下去,把它讀完呢?
我的答案是堅(jiān)持,但允許短暫的修整、停留,然后反復(fù)發(fā)起沖刺,拼搶,直到攻克這個(gè)“山頭”。
因?yàn)橐坏┓艞壘蛯⑶肮ΡM棄,一旦突破,自身能力能得到一個(gè)質(zhì)的飛躍。
我在閱讀Netty源碼時(shí)就遇到了難題,當(dāng)時(shí)剛剛寫完Netty的內(nèi)存泄露檢測(cè),準(zhǔn)備開始研究?jī)?nèi)存分配機(jī)制, 這一塊兒非常抽象,涉及的數(shù)據(jù)結(jié)構(gòu)復(fù)雜,需要掌握二叉樹與數(shù)組之間如何映射、牽扯到大量的位運(yùn)算等等,讓我在探究其原理時(shí)寸步難行,怎么辦呢?放棄?
當(dāng)一周、兩周都無(wú)法取得突破時(shí),人很容易想到:這樣持續(xù)投入時(shí)間,又沒有回報(bào), 是不是在浪費(fèi)時(shí)間?
其實(shí)我想過(guò)放棄,但轉(zhuǎn)念一想:放棄后,我會(huì)做什么呢?玩游戲?看電視?
既然是玩游戲、看電視,不更是浪費(fèi)時(shí)間嗎?想清楚這一層后,繼續(xù)攻關(guān),繼續(xù)突破就成了唯一的選擇。
當(dāng)然,遇到難題了,偶爾放松一兩天,重新調(diào)整狀態(tài)也是非常必要的。短暫的休整可以讓我們變得不那么焦躁,有利于我們重新整理思路,重新發(fā)起沖刺。
當(dāng)時(shí)遇到難題時(shí),采取哪些措施有利于我們攻克難題呢?
1、向“度娘”求救
在閱讀源碼時(shí)可以將看不懂的代碼直接COPY一小段到百度上去搜索,可能會(huì)有大牛已經(jīng)對(duì)這些代碼做過(guò)解讀,能起到指點(diǎn)作用。
當(dāng)時(shí)經(jīng)過(guò)我的搜索,發(fā)現(xiàn)Netty的內(nèi)存分配算法并不是首創(chuàng),而是對(duì) jemalloc 算法的實(shí)現(xiàn),通過(guò)查閱相關(guān)的技術(shù)文檔,可以從整體上理解Netty的內(nèi)存分配算法。
2、Debug
Netty追求極致的性能,采用了大量的位運(yùn)算,由于平時(shí)工作中很少會(huì)使用位運(yùn)算,看起來(lái)比較吃力,為了彌補(bǔ)這方面的不足,使用DEBUG模式,結(jié)合運(yùn)行時(shí)數(shù)據(jù),去理解位運(yùn)算,更容易開竅。
經(jīng)過(guò)上面的努力,花了10天時(shí)間,Netty的內(nèi)存分配機(jī)制慢慢被我解開,經(jīng)過(guò)分解,我寫了一系列文章來(lái)描述Netty內(nèi)存分配:
邁過(guò)了這道坎,后面的源碼閱讀效率變得非常高效。
通過(guò)攻克一個(gè)個(gè)難題,最終從量變引發(fā)質(zhì)變,逐漸形成一條自己的源碼閱讀理論,后續(xù)的源碼分析RocketMQ、Dubbo、ElasticJob、Sentinel、Kafka等專欄就變得非常簡(jiǎn)單了。
我初期的源碼閱讀文章基本上是記流水賬,例如對(duì)源碼一樣一行加注釋,只關(guān)注底層實(shí)現(xiàn)細(xì)節(jié),但并未形成更高層次認(rèn)知,對(duì)其設(shè)計(jì)理念沒有提煉與深度領(lǐng)悟。
隨著技術(shù)類文章的持續(xù)分享, 我認(rèn)識(shí)了很多大牛,發(fā)現(xiàn)和他們交流的時(shí)候,發(fā)現(xiàn)他們一開始并不會(huì)說(shuō)細(xì)節(jié),而是講設(shè)計(jì)理念。
這就要求我們?cè)陂喿x源碼的時(shí)候多思考,并反問(wèn)自己如果自己實(shí)現(xiàn)的話該如何著手,如何設(shè)計(jì),帶著疑問(wèn)去研究源碼。通過(guò)對(duì)比,思考,會(huì)對(duì)其背后的理念有了更深刻的理解。
不管是哪個(gè)開源框架,都會(huì)存在BUG或者實(shí)現(xiàn)并不合理的地方,如果大家在閱讀源碼的時(shí)候能夠深入思考, 合理質(zhì)疑,并能通過(guò)驗(yàn)證證明自己的觀點(diǎn),然后與官方取得聯(lián)系,交流,共同促進(jìn)社區(qū)的發(fā)展,說(shuō)明我們的能力、思考得到了極大的提升。
思考與質(zhì)疑是源碼閱讀的一個(gè)升華,比如我在看Sentinel 熔斷時(shí)對(duì)其提出的質(zhì)疑。
一個(gè)“用戶信息查找”服務(wù),被部署到了三個(gè)機(jī)器上。
其中192.168.1.3這個(gè)機(jī)器有一段時(shí)間負(fù)載過(guò)高,響應(yīng)時(shí)間過(guò)長(zhǎng),發(fā)往它的請(qǐng)求有30%都失敗了。
而30%恰恰是Sentinel設(shè)置的熔斷錯(cuò)誤率, 于是Sentinel認(rèn)為整個(gè)“用戶信息查找”服務(wù)不可用了,熔斷了。
這明顯是不合理的,因?yàn)?92.168.1.4和192.168.1.5這兩個(gè)機(jī)器還活著呢!
本質(zhì)上這是因?yàn)槿蹟噱e(cuò)誤率被定義到了服務(wù)級(jí)別 :服務(wù) -> 熔斷錯(cuò)誤率
在對(duì)這個(gè)問(wèn)題進(jìn)行思考的時(shí)候,我認(rèn)為在配置熔斷規(guī)則的時(shí)候,需要細(xì)化,應(yīng)該把熔斷錯(cuò)誤率定義到資源組級(jí)別:[服務(wù) , 機(jī)器] -> 熔斷錯(cuò)誤率
這樣,當(dāng)一個(gè)機(jī)器上的服務(wù)提供者被熔斷后,不影響其他機(jī)器,保證了真正的高可用。
通過(guò)與官方聯(lián)系,我這個(gè)想法得到了其作者的肯定,反向促進(jìn)了社區(qū)的發(fā)展。
這篇文章來(lái)自于我的好基友的公眾號(hào) 「中間件興趣圈」,由《RocketMQ技術(shù)內(nèi)幕》作者,RocketMQ社區(qū)優(yōu)秀布道師維護(hù),主打成體系分享JAVA主流中間件,打造完備的互聯(lián)網(wǎng)架構(gòu)體系,目前涵蓋Java并發(fā)、微服務(wù)、消息、調(diào)度、數(shù)據(jù)異構(gòu)等領(lǐng)域,未來(lái)繼續(xù)關(guān)注監(jiān)控、在線診斷等領(lǐng)域。
聯(lián)系客服