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