使用開源專案的正確姿勢,都是血和淚的總結!
軟體開發領域有一個流行的原則:DRY,Don’t repeat yourself,我們翻譯過來更形象通俗:不要重複造輪子。開源專案主要目的是共享,其實就是為了讓大家不要重複造輪子,尤其是在網際網路這樣一個快速發展的領域,速度就是生命,引入開源專案,可以節省大量的人力和時間,大大加快業務的發展速度,何樂而不為呢?
然而現實往往沒有那麼美好,開源專案雖然節省了大量的人力和時間,但帶來的問題也不少,相信絕大部分同學都踩過開源軟體的坑,小的影響可能是宕機半小時,大的問題可能是丟失幾十萬資料,甚至災難性的事故是全部資料都丟失。
除此以外,雖然DRY原則擺在那裡,但實際上開源專案反而是最不遵守DRY原則的,重複的輪子好多,尤其是歪果仁,一看哪個開源方案不爽,自己就吭哧吭哧搞一個差不多的:你有MySQL,我有PostgreSQL;你有MongoDB,我有Cassandra;你有memcached,我有redis;你有Gson,我有Jackson;你有Angular,我有React。。。。。。總之放眼望去,其實相似的輪子很多!相似輪子太多,選擇就是讓人頭疼的問題了。
怎麼辦?完全不用開源專案幾乎是不可能的,我們需要更加聰明的去選擇和使用開源專案。形象點說:不要重複發明輪子,但要找到合適的輪子!你開的是保時捷,可別找個拖拉機的輪子。
接下來我將根據加入UC 5年與開源專案有關的經歷,總結出一些“如何正確使用開源專案”的經驗和教訓。有的專案是我親身經歷,有的是我接觸到的,有的是我觀察的,其中部分描述細節可能並不完全準確,大家可以結合自己的經歷一起探討。
以下內容主要分3個部分進行描述,分別是“選”、“用”、“改”。
選:如何選擇一個開源專案
【聚焦是否滿足業務】
我們在選擇開源專案的時候,一個頭疼的問題就是相似的開源方案較多,而且後面的總是要宣稱比前面的更加牛逼。我們在選擇的時候有點無所適從,總是會擔心選擇了A方案而錯過了B方案,或者反過來。這裡我們的經驗是聚焦於是否滿足業務,而不需要過於關注開源方案是否牛逼。
案例:當時嘗試一個社交類業務時,我們發現了TT(Tokyo Tyrant)這個開源方案,覺得既能夠做快取取代Memcached,又有持久化儲存功能,可以取代MySQL,很牛逼,很高大上,於是就在業務裡面大量使用了。但後來的使用過程讓人很蛋疼,主要表現為:
1)不能完全取代MySQL,因此有兩份儲存,設計的時候每次都要討論和決策
2)功能上看起來很高大上,但相應的bug也不少,而且有的bug是致命的,例如所有資料不可讀,後來是自己研究原始碼寫了一個工具才恢復了部分資料。
3)功能確實牛逼,但需要花費較長時間熟悉各種細節
後來我們反思和總結,其實當時的業務Memcached + MySQL完全能夠滿足,且大家都熟悉,當時的業務完全不需要引入TT。
簡單來說:如果你的業務要求1000 TPS,那麼一個20000 TPS 和50000 TPS的方案是沒有區別的。有的人可能會擔心我TPS不斷上漲怎麼辦?其實不用擔心,我們的架構會不斷演進的,等到真的需要這麼高的時候我們再來架構重構,記住:不要過早優化,過早優化是萬惡之源 —— 《UNIX程式設計哲學》
【聚焦是否成熟】
很多新的開源專案往往都會聲稱自己比以前的專案更加牛逼:效能更高、功能更強、引入更多新概念。。。。。。看起來都很誘人,但實際上都有意無意的隱藏了一個負面的問題:都更加不成熟!不管多牛逼的程式設計師寫出來的專案都會有bug,千萬不要以為作者牛逼就沒有bug,Windows、Linux、MySQL的開發者都是頂級的開發者吧,一樣很多bug。
不成熟的開源專案應用到生產環境,風險極大。輕則宕機,重則宕機後重啟都恢復不了,更嚴重的是資料丟失都找不回了。還是以上面提到的TT為例:我們真的遇到異常斷電後,檔案被損壞,重啟也恢復不了的故障,還好當時每天做了備份,於是只能用1天前的資料進行恢復,但當天的資料全部丟失了。後來我們花費了大量的時間和人力去看原始碼,自己寫工具恢復了部分資料,還好這些資料不是金融相關的資料,丟失一部分問題也不大,否則就有大麻煩了。
所以在選擇開源專案的時候,儘量選擇成熟的開源專案,降低風險,形象點說:寧要2.0的熟女,不要0.2的處女!
可以從以下幾個方面考察是否成熟:
1)版本號:一般建議除非特殊情況,否則不要選0.X版本的,至少選1.X版本的,版本號越高越好。
2)使用的公司數量:一般開源專案都會把採用了自己專案的公司列在主頁上,公司越大越好,數量越多越好
3)社群活躍度:看看社群是否活躍,發帖數、回覆數、問題處理速度等
【聚焦運維能力】
我們在選擇開源專案的時候,基本上都是聚焦於技術指標,例如效能、可靠性、功能這些方案,而幾乎不會去關注運維方面的能力。但如果要將方案應用到線上生產環境,運維能力是必不可少的一環,否則一旦出問題,運維、研發、測試都只能乾瞪眼,求菩薩保佑了!
可以從以下幾個方案去考察運維能力:
1)開源方案日誌是否齊全:有的開源方案日誌只有寥寥啟動停止幾行,出了問題根本無法排查
2)開源方案是否有命令列、管理控制檯等維護工具,能夠看到系統執行時的情況
3)開源方案是否有故障檢測和恢復的能力,例如告警、倒換等
用:如何使用開源方案
【深入研究,仔細測試】
很多人用開源專案,其實是完完全全的“拿來主義”,看了幾個Demo,把程式跑起來就開始部署到線上應用了。就好像看了一下開車指南,知道了方向盤是轉向、油門是加速、剎車是減速,然後就開車上路了,其實是非常危險的。
案例:我們有團隊使用了elasticsearch,基本上是拿來就用,倒排索引是什麼不太清楚,配置都是用預設值,跑起來就上線了,結果就遇到節點ping時間太長,剔除異常節點太慢,導致整站訪問掛掉。
案例2:UC很多團隊最初使用MySQL的時候,也沒有怎麼研究過,經常有業務部門抱怨MySQL太慢了,其實經過定位,發現最關鍵的幾個引數(例如innodb_buffer_pool_size, sync_binlog,innodb_log_file_size等)都沒有配置或者配置錯誤,效能當然會慢。
可以從如下幾方面進行研究和測試:
1)通讀開源專案的設計文件或者白皮書,瞭解其設計原理
2)核對每個配置項的作用和影響,識別出關鍵配置項
3)進行多種場景的效能測試
4)進行壓力測試,連續跑幾天,觀察cpu、記憶體、磁碟io等指標波動
5)進行故障測試:kill,斷電、拔網線、重啟100次以上、倒換等
【小心應用,灰度釋出】
假如我們做了上面的“深入研究、仔細測試”,發現沒什麼問題,是否就可以放心大膽的應用到線上了呢?別高興太早,即使你的研究再深入,測試再仔細,也還是要小心為妙,因為再怎麼深入的研究,再怎麼仔細的測試,都只能降低風險,但不可能完全覆蓋所有線上場景。
案例:還是以TT為例吧,其實我們在應用之前專門安排一個大牛看原始碼、做測試,做了大約1個月,但最後上線還是遇到各種問題。線上生產環境的複雜度,真的不是測試能夠覆蓋的,必須小心謹慎。
所以,不管研究多深入、測試多仔細、自信心多爆棚,時刻對線上要有敬畏之心,小心駛的萬年船。我們的經驗就是先在非核心的業務上用,然後有經驗後慢慢擴充套件。
【做好應急,以防萬一】
即使我們前面的工作做得非常完善和充分,也不能認為就萬事大吉了,尤其是剛開始使用一個開源專案,運氣不好的話就可能遇到一個之前全世界的使用者從來沒遇到的bug,導致業務都無法恢復,尤其是儲存方面,一旦出現問題無法恢復可能就是致命的打擊。
案例(此案例是聽說的):某個業務使用了MongoDB,結果宕機後部分資料丟失,無法恢復,也沒有其它備份,人工恢復都沒辦法,只能接一個使用者投訴處理一個,導致DBA和運維從此以後都反對我們用MongoDB,即使是嘗試性的。
雖然因為一次故障就完全反對嘗試是有點反應過度了,但確實故障也給我們提了一個醒:對於重要的業務或者資料,使用開源專案時,最好有另外一個比較成熟的方案做備份,尤其是資料儲存。例如:如果要用MongoDB或者Redis,可以用MySQL做備份儲存。這樣做雖然複雜度和成本高一些,但關鍵時刻能夠救命!
改:如何基於開源專案做二次開發
【保持純潔,加以包裝】
當我們發現開源專案有的地方不滿足我們的需求的時候,自然會有一種去改改的衝動,但是怎麼改是個大學問。一種方式是投入幾個人從內到外全部改一遍,將其改造成完全符合我們業務需求。但這樣做有幾個比較嚴重的問題:
1)投入太大,一般來說,redis這種級別的開源方案,真要自己改,至少要投入2個人,搞個1個月以上
2)失去了跟隨原方案演進的能力:改的太多的話,即使原有開源專案繼續演進,我們也無法合併了,因為差異太大。
所以我們的建議是不要改動原系統,而是要開發輔助系統: 監控,報警,負載均衡,管理等。以Redis為例,如果我們想增加叢集功能,不要去改動Redis本身的實現,而是增加一個proxy層來實現,Twitter的Twemproxy就是這樣做的,而Redis到了3.0後本身提供了叢集功能,原有的方案簡單切換到Redis 3.0即可。詳細可參考(http://www.cnblogs.com/gomysql/p/4413922.html )
如果實在想改到原有系統,怎麼辦呢?我們的建議是直接給開源專案提需求或者bug,但弊端就是響應比較緩慢,這個就要看業務緊急程度了,如果實在太急那就只能自己改了,不過不是太急,建議做好備份或者應急手段即可。
(注:上圖是“芙蓉姐姐”,看不出來吧:) )
【發明你要的輪子】
這點估計讓很多人大跌眼鏡,怎麼講了半天,最後又回到了“重複發明你要的輪子”呢?
其實選與不選開源專案,核心還是一個成本和收益的問題,並不是說選擇開源專案就一定是最優的方案,最主要的問題是:沒有完全適合你的輪子!
軟體領域和硬體領域最大的不同就是軟體領域沒有絕對的工業標準,大家都很盡興,想怎麼玩怎麼玩,不像硬體領域,你造一個尺寸與眾不同的輪子,其它車都用不上,你的輪子工藝再高,質量再好也是白費;軟體領域可以造很多相似的輪子,也基本上能到處用,例如你把快取從Memcached換成Redis,不會有太大的問題。
除此以外,開源專案為了能夠大規模應用,考慮的是通用的處理方案,而不同的業務其實差異較大,通用方案並不一定完美適合具體的某個業務。比如說Memcached,通過一致性hash提供叢集功能,但是我們的一些業務,快取如果有一臺宕機,整個業務可能就被拖慢了,這就要求我們提供快取備份的功能,但Memcached又沒有,而Redis當時又沒有叢集功能,於是我們投入2~4個人花了大約2個月時間基於LevelDB的原理,自己做了一套快取框架支援儲存、備份、叢集的功能,後來又在這個框架的基礎上增加了跨機房同步的功能,很大程度上提升了業務的可用性水平。如果完全採用開源方案,等開源方案來實現,是不可能這麼快速的,甚至都有可能開源專案完全就不支援我們的需求。
所以,如果你有錢有人有時間,投入人力去重複發明完美符合自己業務特點的輪子也是很好的選擇!畢竟,土豪們(BAT......等)很多都是這樣做的,否則的話我們也就沒有那麼多好用的開源專案了 :)
注:首發雲棲,同步CSDN和阿里ATA,InfoQ、阿里雲、阿里百川轉發的就是這篇