天天看點

阿裡畢玄:提升代碼能力的4段經曆

阿裡畢玄:提升代碼能力的4段經曆

作者 | bluedavy

來源 | 阿裡技術公衆号

第一段:第一次感受每天億級系統的挑戰

2008年,HSF的第二個版本,在當時淘寶最重要的交易中心上線,上線當天造成淘寶網站通路巨慢,交易類的頁面幾乎打不開,最後靠下線HSF才恢複。

下線後開始查問題,HSF的第二個版本基于的是JBoss Remoting,JBoss Remoting在當時的版本裡遠端同步調用的逾時時間是寫死在代碼裡的60s,而調用的服務确實會有一些超過10幾秒的現象出現,導緻了Web應用處理Web請求的線程池被這些慢請求給逐漸占據,請求堆積,最終呈現出了頁面打開非常慢的現象。

查清原因後,決定基于當時的Mina重寫整個HSF的通信。重寫的這兩個月時間對我自己寫代碼的能力有很大的提升,無論是對網絡IO方面處理的深入學習,還是在高并發系統上的深入學習。現在想想學習的方式也就是翻各類網絡IO的科普資料,然後是讀Mina的源碼、Java網絡IO的源碼。并發這塊的學習主要還是靠那本經典的《Java并發程式設計實戰》,以及讀Java J.U.C裡的代碼。這段時間的學習相比以往翻《Think in Java》之類的最大差別是,學習後付諸實踐,随着HSF這個新的重寫的版本的上線,基本算是逐漸真正掌握了這些部分的代碼能力。

除了代碼能力的提升外,得到了另外一個最大的教訓就是,對于一個億級且長時間運作的系統,很多看起來的小機率的問題都一定會成為嚴重的問題。這也是寫高并發系統難的原因,要求必須對自己寫的代碼,以及自己代碼調用到的各種API裡的實作都非常的清楚,這樣才能真正確定最終代碼的魯棒性。

第二段:民間"消防隊"的故事

第二段對我自己寫代碼能力提升特别大的經曆是在民間"消防隊"的那段日子。淘寶在2009年故障特别多,但處理故障還沒有一個标準的體系群組織,導緻很多時候會出現故障出了都沒什麼人處理,或者處理效率不高。于是當時有個運維團隊的同學拉了一些人組建了一個群,群的名字叫淘寶消防隊,用來處理淘寶出現的各種故障,我很湊巧的也加入了這個群,這個群裡還有另外一個整個阿裡公認的超級技術大神:多隆。

一開始看到各種故障的時候,壓根就不知道怎麼下手。處理故障需要的通常不僅僅是寫代碼的能力,還需要對一個系統的全貌要有一定的掌握。例如前幾年一篇特别火的文章,點選搜尋背後發生了什麼,其實就是要對一個系統的處理流程特别的熟悉,這在處理故障的時候是非常重要的。在了解了故障大概在哪個環節後,很重要的就是對這個環節代碼運作機制的細節的掌控了,這個時候通常來說運用各種工具是非常重要的,可以有效地幫助你知道具體發生了什麼,例如像系統層面的top -H之類的,Java層面的BTrace等等,都可以讓你根據運作情況去定位問題。

這段時間我覺得我的提升就是靠大量的練手。故障确實有點多,一開始就靠看别人怎麼處理,主要是從多隆那裡學,然後是嘗試自己解決一些故障,解決的越來越多後慢慢熟練度就上去了。除了解決故障能力的提升外,由于看了很多由代碼層面造成的故障,對自己在寫代碼時如何更好的保證魯棒性來避免故障,也是非常有幫助的。例如,我看過很多濫用線程池造成建立了大量線程,最終導緻線程建立不出來的case,就會明白自己在用線程池的場景裡一定要非常清楚地控制最大的數量,包括堆積的政策等。又例如,我看過N多的因為自增長容量的資料結構導緻的OOM的case,就會明白在寫代碼的時候不能認為一定不會發生資料結構增長到超級大,是以不做任何保護的case。這段時間我明白到的就是,寫一段能運轉、實作需求的代碼不難,但要寫一段在各種情況下都能長期穩定運作的代碼是真心不容易,我覺得這是一個職業的寫商業系統的程式員和隻寫程式玩玩的程式員的最大差别。

第三段:重寫通信架構

2010年,我從中間件團隊離開,去做HBase。那個時候的HBase裡面的通信還是用一個非常簡單的寫法實作的。我想着要麼就把以前HSF裡用的移植到HBase裡用,這個時候剛好多隆在用C給各類C的應用寫一個通用的通信架構libeasy,于是就有了一次測試,我記得第一次測試的結果,就看到了原來HSF裡面的通信架構的高并發能力和libeasy比相差無比巨大。我便和多隆探讨他是怎麼實作的,我看看能不能學習下,在Java這邊的版本裡也改改,是以有了這段重寫通信架構的經曆。

本來以為之前在寫HSF的那幾年應該算是對通信架構這塊的代碼相關的能力掌握的不錯了,在和多隆一起重寫的這段過程中,才發現差距還是很大的。多隆教會了我很多細節的問題,基于NIO的通信架構的核心是用非常少的IO線程來處理IO事件(太多也沒用,因為有些部分就隻能串行),是以如何高效的使用好這幾個IO線程是非常關鍵的,要盡量減少這幾個IO線程處理一些不相關的動作,另外一點就是盡量減少IO線程和業務處理線程的切換,例如後來常見的批量把一個流裡的多個請求一次性丢給業務處理線程。

這段經曆對自己在代碼邏輯整體的細節層面更加深入地掌握是非常有幫助的,這對于寫要求很高的系統是非常重要的,畢竟對于一個超大規模的系統而言,1%的提升還是可觀的。

第四段:學習JVM

之前因為處理故障比較多,有段時間我開始給公司同僚們分享如何處理故障,後來發現有些問題自己也講不清楚,或者也不知道怎麼處理,必須深入學習JVM才行,但其實一開始我完全摸不着門路,JVM代碼打開都不知道從哪看起。

很幸運,碰到了一個同樣愛好JVM又比我強很多的同學,就是撒迦,圈内通常叫R大。我和撒迦好幾個周末約着在公司一起看JVM代碼,有撒迦的指點,我終于算是入門了,知道大概怎麼去看了,而且兩個人一起看代碼,互相分享和探讨,效率是非常高的。

有了這段經曆,再加上繼續處理着一些故障,基本上逐漸對JVM的代碼實作有了更多的了解。在後來做故障分享、問題解決什麼的時候終于能更好地做到知其然知是以然。同樣,這對處理故障的能力、寫代碼的能力也是非常有幫助的,例如會更加明白以前認為的所謂的面向GC友好的代碼是幾個意思。也有了更深的感受,就是其實Java的代碼呢,通常不會寫的太爛,因為JVM在運作期會做很多的盡可能的優化,拉到一個平均線,但要寫得很好,難度是非常大的,因為需要懂JVM,懂JVM下面的OS。

總結

其實也總結不出什麼,因為每個人所處的環境不一樣,有不同的适合各自提升的方法。我看自己的經曆總結下來,我覺得:

  • 如果環境不具備,就給自己一個命題挑戰。例如要學高并發的通信,可以嘗試自己寫一個和其他的做對比,做性能等的PK,這個通常提升還是會很大的。要學GC,可以嘗試給自己幾個題目,來控制GC的行為等,如果環境具備的話,确實會更加有利。
  • 多和優秀的程式員一起學習。我自己從多隆、撒迦身上學習到了很多很多。從很多優秀的開源代碼,像Netty、OpenJDK裡面也學習到了很多很多,是以多參與一些優秀的開源項目也是一個很好的提升方法,看優秀的書(例如并發裡的那本《Java并發程式設計實戰》,JVM裡的《Oracle JRockit: The Definitive Guide》,《深入了解Java虛拟機》等),也一樣是一種向優秀程式員學習的好方法。
  • 多多嘗試解決問題/故障。這絕對是提升代碼綜合能力非常好的一個方法,自己工作裡機會少的話,網上有大把的平台,像Stack Overflow之類的,都是很好的練習場。

最後的最後,我還是想說,代碼能力作為程式員的硬名片,始終是最有效的區分程式員能力的東西,"talk is cheap, show me the code",這句話我覺得是永遠成立的。