前言
從頂級公司的面試說起
網際網路頂級公司是當今很多開發人員,尤其是應屆畢業生們所嚮往的公司。但大家應該都聽過關於頂級公司面試候選人的一句調侃的話,「面試造火箭,工作轉螺絲」,這雖然有一點誇張的成分,不過也確實描述得比較形象。在面試中,尤其是頂級網際網路頂級公司的面試,對技術的詢問往往很深。但是到了工作中,可能確實又需要花不少時間在寫各種各樣的重複 CRUD 上。
那為什麼會出現這種情況,是頂級公司閒得沒事非得為難候選人嗎?其實不是,這是因為紮實的底層功力確實對頂級公司來說很重要。
網際網路頂級公司區別於小公司的業務特點就是巨量請求,隨便一個業界第二等級的App,每天的後端介面請求數過億很常見,更不用提微信、淘寶等頂級公司了。在這種量級的使用者請求下,業務能7×24 小時穩定地提供服務就非常重要了。哪怕服務故障出現十分鐘,對業務造成的損失可能都是不容小覷的。
所以在頂級公司中,你寫出來的程式不是能跑起來就行了,是必須能夠穩定運行。程式在運行期間可能會無法避免地遭遇各種線上問題。應用都是跑在硬體、作業系統之上的,因此線上的很多問題都和底層相關。
如果遇到線上問題,你是否有能力快速排除和處理?例如有的時候線上存取逾時是因為TCP 的全連接佇列滿導致的,如果你對這類底層的知識了解得不夠,則根本無法應對。
另外,頂級公司應徵高水準程式設計師的目的可能不僅是能快速處理問題,甚至希望程式設計師能在寫程式之前就做出預判,從而避免出故障。
不知道你所在的團隊是否進行過Code Review(程式評審,簡稱CR)。
往往新手程式設計師自我感覺良好、覺得寫得還不錯的程式給資深程式設計師看一眼就能發現很多上線後可能會出現的問題。
頂級公司在招人上是不怕花錢的,最怕的是業務不穩定和不可靠。如果以很低的價錢招來水準一般的程式設計師,結果導致業務三天兩頭出問題,給業務收入造成損失,那可就得不償失了。所以,要想進頂級公司,紮實的內功是不可或缺的。
談談工作以後的成長
那是不是說已經工作了,或已經進入頂級公司了,紮實的內功、能力就可有可無了呢?答案當然是否定的,工作以後內功也同樣的重要!
拿後端開發工作來舉例。初接觸後端開發的朋友會覺得,這個方向太容易了。我剛接觸後端開發的時候也有這種錯覺。我剛畢業做Windows 下的C++ 開發的時候,專案裡的程式編譯完生成的專案都是幾個GB 的,但是轉到後端後發現,一個服務端介面可能100 行程式就搞定了。
由於看上去的這種「簡單性」,許多工作三年左右的後端開發人員會陷入一個成長瓶頸,手頭的東西感覺已經特別熟練了,程式語言、框架、MySQL、Nginx、Redis 都用得很熟,總感覺自己沒有什麼新東西可以學習了。
他們真的已經掌握了所有了嗎?其實不然,當他們遇到一些線上的問題時,排除和定位方法又極其有限,很難承擔得起線上問題緊急救火的重要責任。當程式性能出現瓶頸的時候,只是在網上搜幾篇發文,瞎子摸
象式地試一試,各個一知半解的核心參數調一調,對關鍵技術缺乏足夠深的認知。
反觀另外一些工作經驗豐富的高級技術人員,他們一般對底層有著深刻的理解。當線上服務出現問題的時候,都能快速發現關鍵問題所在。就算是真的遇到了棘手的問題,他們也有能力潛入底層,比如核心原始程式,去找答案,看看底層到底是怎麼幹的,為什麼會出現這種問題。
所以頂級公司不僅是在應徵時考察面試者,在內部的晉升選拔中也同樣注重考察開發人員對於底層的理解以及性能把控的能力。一個人的內功深淺,決定了他是否具備基本的問題排除以及性能最佳化能力。內功指的就是當年你曾經學過的作業系統、網路、硬體等知識。網際網路的服務都是跑在這些基礎設施之上的,只有你對它們有深刻的理解,才能夠源源不斷想到新的性能分析和最佳化辦法。
所以說,紮實的內功並不是透過頂級公司面試以後就沒有用了,而是會貫穿你整個職業生涯。
再聊聊中年焦慮
之前網路曾爆炒一篇標題為「網際網路不需要中年人」的文章,瘋狂繪製35 歲藍領程式設計師的前程問題,製造焦慮。本來我覺得這件事情應該只是媒體的炒作行為而已,不過恰恰兩、三年前我們團隊擴充,需要應徵一些等級高一點的開發人員,之後使我對此話題有了些其他想法。
那段時間我面試了七十多人,其中有很多工作七、八年以上的。
我面試的這些人裡,有這麼一部分人雖然已經工作了七、八年以上,但是所有的經驗都集中在手頭的那點專案的業務邏輯上。對他們稍微深入問一點性能相關的問題都沒有好的想法,技術能力並沒有隨著工作年限的增長而增長。換句話說,他們並不是有七、八年經驗,而是把兩、三年的經驗用了七、八年而已。
和這些人交流後,我發現共同的原因就是他們絕大部分的時間都是在處理各種各樣的業務邏輯和bug,沒有時間和精力去提升自己的底層技術能力,真遇到線上問題也沒有耐心鑽研下去,隨便在網上搜幾篇文章都試一試,哪個碰對了就算完事,或乾脆把故障拋給運行維護人員去解決,導致技術水準一直原地踏步,並沒有隨著工作年限而同步增長。我從那以後也確實意識到,藍領程式設計師圈裡可能真的有中年焦慮存在。
那是不是這種焦慮就真的無解了呢?答案肯定「不是」。至少我面試過的這些人裡還有一部分很優秀,不但業務經驗豐富,而且技術能力出眾,目前都發揮著重要作用。你也可以看看你們公司的高等級技術人員,甚至業
界的各位技術大神,相信他們會是你們公司甚至業界長期的中流砥柱。
那麼工作了多年的這兩類人中,差異如此巨大的原因是什麼呢?我思考了很多,也和許多人都討論過這個問題。最後得出的結論就是大神們的技術累積是隨著工作年限的增長而逐漸增長的,尤其是內功,和普通的開發人員相差巨大。
大神們對底層的理解都相當深刻。深厚的內功知識又使得他們學習起新技術來非常快。舉個例子,在初級開發人員眼裡,可能Java 的NIO 和Golang 的net 套件是兩個完全不同的東西,所以學習起來需要分別花費不少精力。但在底層知識深厚的人眼裡,它們兩個只不過是對epoll 的不同封裝方式,就像只換了一身衣服,理解起來自然就輕鬆得多。
如此良性迭代下去,技術好的和普通的開發人員相比,整體技術水準差距越拉越大。普通開發人員越來越焦慮,甚至開始擔心技術水準被剛畢業的年輕人超越。
修煉內功的好處內功,它不幫你掌握最新的開發語言,不教會你時髦的框架,也不會帶你走進火熱的人工智慧,但是我相信它是你成為「大神」的必經之路。
我簡單列一下修煉內功的好處。
1) 更順利地透過頂級公司的面試。頂級公司的面試對技術的詢問比較底層,而網上的很多答案層次都還比較淺。拿三次握手舉例,一般網上的答案只說到了初步的狀態流轉。其實三次握手中包含了非常多的關鍵技術點,比如全連接佇列、半連接佇列、防syn flood 攻擊、佇列溢位封包遺失、逾時重發等深層的知識。再拿epoll 舉例,如果你熟悉它的內部實現方式,理解它的紅黑樹和就緒佇列,就知道它高性能的根本原因是讓處理程序大部分時間都在處理使用者工作,而非頻繁地切換上下文。如果你的內功能深入觸達這些底層原理,一定會為你的面試加分不少。
2) 為性能最佳化提供充足的「彈藥」。目前大公司內部對於高級和高級以上工程師晉升時考核心的重要指標之一就是性能最佳化。在對核心缺乏認識的時候,大家的最佳化方式一般都是瞎子摸象式的,方法非常有限,做法很片面。當你對網路整體收發送封包的過程理解了以後,對網路在CPU、記憶體等方面的消耗的理解將很深刻。這會對你分析專案中的性能瓶頸所在提供極大的幫助,從而為你的專案性能最佳化提供充足的「彈藥」。
3) 內功方面的技術生命週期長。Linux 作業系統 1991 年就發佈了,現在還是發展得如火如荼。對於作者Linus,我覺得他也有年齡焦慮,但他可能焦慮的是找不到接班人。反觀應用層的一些技術,尤其是很多的框架,生命週期能超過十年我就已經覺得它很棒了。如果你的精力全部押寶在這些生命週期很短的技術上,你說能不焦慮嗎!所以我覺得戒掉浮躁,踏踏實實練好內功是你對抗焦慮的解藥之一。
4) 內功深厚的人理解新技術非常快。不用說業界的各位「大神」了,就拿我自己來舉兩個小例子。我其實沒怎麼翻過Kafka 的原始程式,但是當我研究完了核心是如何讀取檔案的、核心處理網路封包的整體過程後,就「秒懂」了Kafka 在網路這塊為什麼性能表現很突出了。還有,當我理解了epoll 的內部實現以後,回頭再看Golang 的net 套件,才切切實實看懂了絕頂精妙的對網路IO 的封裝。所以你真的弄懂了Linux 核心的話,再看應用層的各種新技術就猶如戴了透視鏡一般,直接看到骨骼。
5) 核心提供了優秀系統設計的實例。Linux 作為一個經過千錘百煉的系統,其中蘊含了大量的世界頂級的設計和實現方案。平時我們在自己的業務開發中,在編碼之前也需要先進行設計。比如我在剛工作的時候負責資料獲取任務排程,其中的實現就部分參考了作業系統處理程序排程方案。再比如,如何在管理巨量連接的情況下仍然能高效發現某一條連接上的IO 事件,epoll 內部的「紅黑樹 + 佇列」組合可以給你提供一個很好的參考。這種例子還有很多很多。總之,如果能將Linux 的某些優秀實現搬到你的系統中,會極大提升你的專案的實現
水準。
時髦的東西終究會過時,但紮實的內功將伴隨你一生。只有具備了深厚的內功底蘊,你才能在發展的道路上走得更穩、走得更遠。
為什麼要寫這本書
平時大家都是用各種語言進行業務邏輯的程式撰寫,無論你用的是PHP、Go,還是Java,都屬於應用層的範圍。但是應用層是建立在物理層和核心層之上的。我把在應用層的技術能力稱為外功,把 Linux 核心、裝置物理結構方面的技術能力稱為內功。前面已經說了,無論是在職業生涯的哪個階段,紮實的內功都很重要。
那好,既然內功如此重要,那就找一些底層相關的資料加強學習就行了。但很遺憾,我覺得目前市面上的技術資料在內功方向存在一些不足。
先說網上的技術文章。目前網上的技術文章、部落格非常多。大家遇到問題往往先去搜一下,但是你有沒有發現,網上入門級資料一搜一大把,而內功深厚、能深入底層原理的文章卻十分匱乏。
比如,現在的網際網路應用大部分都是透過TCP 連接來工作的,那麼一台機器最多能撐多少個TCP 連接?按道理說,整個業界都在講高併發,這應該算是很入門的問題了。但當年我產生這個疑問的時候,在搜尋引擎上搜了個遍也沒找到令我滿意的答案,後來我乾脆自己動手,花了一個多月時間邊做測試,邊挖核心原始程式,才算是把問題徹底弄明白了。
再比如,大部分的開發人員都搞過網路相關的開發。那麼一個網路封包是如何從網路卡到達你的處理程序的?這個問題表面上看起來簡單,但實際上很多性能最佳化方案都和這個接收過程有關,能不能深度理解這個過程決定了你在網路性能上有多少最佳化措施可用。例如多佇列網路卡的最佳化方案是在硬體中斷這一步開始將工作分散在多個 CPU 核心上,進而提升性能的。我幾年前想把這個問題徹底弄清楚,幾乎搜遍了網際網路,翻遍了各種經典書都無法找到想要的答案。
還比如,網上搜到的三次握手的技術文章都是在說一些簡單的內容,用戶端如何發起 SYN 握手進入 SYN_SENT 狀態,服務端回應SYN 並回覆SYNACK,然後進入 SYN_RECV⋯⋯諸如此類。但實際上,三次握手的過程執行了很多核心操作,比如用戶端通訊埠選擇、重傳計時器啟動、半連接佇列的增加和刪除、全連接佇列的增加和刪除。線上的很多問題都是因為三次握手中的某一個環節出問題導致的,能否深度理解這個過程直接決定你是否有線上上快速消滅或避免這種問題的能力。網上能深入介紹三次握手的文章太少了。
你可能會說,網上的文章不足夠好,不是還有好多經典書嗎?首先我得說,電腦類的一些經典的書確實很不錯,值得你去看,但是這裡面存在幾個問題。
一是底層的書都寫得比較深奧難懂,你看起來需要花費大量的時間。假如你已經工作了,很難有這麼大區塊的時間去啃。比如我剛開始深入探尋網路實現的時候,買來了《深入理解Linux 核心》、《深入理解Linux 網路技術內幕》等幾本書,利用工作之餘斷斷續續花了將近一年時間才算理解了一個大概。
另外一個問題就是當你真正在工作中遇到一些困惑的時候,會發現很難有一本經典書能直接給你答案。比如在《深入理解 Linux 網路技術內幕》這本書裡介紹了核心中各個元件,如網路卡裝置、鄰居子系統、路由等,把相關原始程式都講了一遍。但是看完之後我還是不清楚一個封包到底是如何從網路卡到應用程式的,一台伺服器到底能支援多少個TCP 連接。
還有個問題就是電腦技術不同於其他學科,除理論外對實踐也有比較高的要求。如果只是停留在經典書裡的理論階段,實際上很多問題根本就不能理解合格。這些書往往又缺乏和實際工作相關的動手實驗,比如對於一台伺服器到底能支持多少個TCP 連接這個問題,我自己就是在做了很多次的實驗以後才算比較清晰地理解了。還有就是如果沒有真正動過手,那你將來對線上的性能最佳化也就無從談起了。
整體來說,看這些經典書不失為一個辦法,但考量時間的花費和對工作問題的精準處理,我感覺效率比較低。所以鑑於此,我決定輸出一些內容,也就有了這本書的問世。
創作想法
雖然底層的知識如此重要,但這類知識有個共同的特點就是很枯燥。那如何才能把枯燥的底層講好呢?這個問題我思考過很多很多次。
2012 年我在騰訊工作期間,在內部KM 技術討論區上發表過一篇文章,叫作《Linux 檔案系統十問》(這篇文章現在在外網還能搜到,因為被搬運了很多次)。當時寫作的背景是「老大」分配給我一個任務,把所有合作方提供的資料裡的圖片檔案都下載並保存起來。我把在工作中產生的幾個疑問進行了追根溯源,找到答案以後寫成文章發表了出來。比如檔案名稱到底存在了什麼地方,一個空檔案到底佔不佔用磁碟空間,Linux目錄下子目錄太多會有什麼問題等等。這篇文章發表出來以後,竟然在全騰訊公司內部傳播開了,反響很大,最後成為了騰訊KM 當年的年度熱文。
為什麼我的一篇簡單的Linux 檔案系統的文章能得到這麼強烈的迴響?後來我在邏輯思維的一期節目裡找到了答案。節目中說最好的學習方式就是你自己要產生一些問題,帶著這些問題去知識的海洋裡尋找答案,當找到答案的時候,也就是你真正掌握了這些知識的時候。經過這個過程掌握的知識是最深刻的,和你自身的融合程度也是最高的,能完全內化到你的能力系統中。
換到讀者的角度來考慮也是一樣的。其實讀者並不是對底層知識感興趣,而是對解決工作中的實際問題興趣很大。這篇文章其實並不是在講檔案系統,而是在講開發過程中可能會遇到的問題。我只是把檔案系統知識當成工具,用它來解決掉這些實際問題而已。
所以我在本書的創作過程中,一直貫穿的是這個想法:以和工作相關的實際的問題為核心。
在每一章中,我並不會一開始就給你灌輸軟體中斷、epoll、socket 核心物件等核心網路模組的知識,我也覺得這些很乏味,而是每章先拋出幾個和開發工作相關的實際問題,然後圍繞這幾個問題展開探尋。是的,我用的詞不是「學習」,而是「探尋」。和學習相比,探尋更強調對要解惑的問題的好奇心,更有意思。
雖然本書中會涉及很多的原始程式,但這裡先強調一下,這並不是一本原始程式解析的書。大家學習的真正目的是理解和解決專案實踐相關的問題,進而提高駕馭手頭工作的能力,而原始程式只是我們達成目的的工具和途徑而已。
適用讀者
本書並不是一本電腦網路的入門書,閱讀本書需要你具備起碼的電腦網路知識。它適合以下讀者:
■想透過提升自己的網路內功而進頂級公司的讀者。
■不滿足於只學習網路通訊協定,也想理解它是怎麼實現的讀者。
■雖有幾年開發工作經驗,但對網路消耗把握不準的開發人員。
■想做網路性能最佳化,但沒有成系統的理論指導的讀者。
■維護各種高併發伺服器的運行維護人員。
其他說明
本書中的內容是在我的微信公眾號「開發內功修煉」的部分內容的基礎上,理順了整體的框架結構整理而來的。歡迎大家關注我的微信公眾號,及時閱讀最新內容。另外,由於本人精力有限,書中內容難免會有疏漏,如您發現內容中有不正確的地方,歡迎到微信公眾號後台或聯繫本人微信批評指正,不勝感激!也歡迎大家加入我的微信交流群,互相學習、共同成長。個人微信帳號為zhangyanfei748528。
致謝
本書能夠得以問世,要感謝許多許多人。首先要感謝的是我的微信公眾號和知乎專欄裡的粉絲們。我提筆寫下第一篇文章的時候,是根本沒敢想能夠成系統出一本書的,是你們的認可和鼓勵支持著我輸出一篇又一篇的硬核心技術文。現在回頭一看,竟然攢了好幾十篇。基於這些文章,將來再整理出一本書都是有可能的。而且很多讀者技術也非常優秀,指出了我的文章中不少的瑕疵。飛哥在此對大家表示感謝!
接下來要感謝的是我的愛人,在我寫作的過程中給了我很大的支持和鼓勵,還幫我分擔了很多顧小孩的工作,讓我能專心地投入到寫作中來。
寫作要投入的精力是巨大的,如果缺少家人的支持,想完成一本書基本是不可能的。
感謝:鞏鵬軍、彭東林、孫國路、王錦、隨行、harrytc、t 濤、point、LJ、WannaCry 等同學提出的非常棒的改進建議!
最後要感謝的是道然科技姚老師以及電子工業出版社的老師們,是你們幫我完成出書過程最後的「臨門一腳」。