天天看點

使用開源項目的正确姿勢,都是血和淚的總結!

軟體開發領域有一個流行的原則: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天前的資料進行恢複,但當天的資料全部丢失了。後來我們花費了大量的時間和人力去看源碼,自己寫工具恢複了部分資料,還好這些資料不是金融相關的資料,丢失一部分問題也不大,否則就有大麻煩了。

是以在選擇開源項目的時候,盡量選擇成熟的開源項目,降低風險。

可以從以下幾個方面考察是否成熟:

1)版本号:一般建議除非特殊情況,否則不要選0.X版本的,至少選1.X版本的,版本号越高越好。

2)使用的公司數量:一般開源項目都會把采用了自己項目的公司列在首頁上,公司越大越好,數量越多越好

3)社群活躍度:看看社群是否活躍,發帖數、回複數、問題處理速度等

我們在選擇開源項目的時候,基本上都是聚焦于技術名額,例如性能、可靠性、功能這些方案,而幾乎不會去關注運維方面的能力。但如果要将方案應用到線上生産環境,運維能力是必不可少的一環,否則一旦出問題,運維、研發、測試都隻能幹瞪眼,求菩薩保佑了!

可以從以下幾個方案去考察運維能力:

1)開源方案日志是否齊全:有的開源方案日志隻有寥寥啟動停止幾行,出了問題根本無法排查

2)開源方案是否有指令行、管理控制台等維護工具,能夠看到系統運作時的情況

3)開源方案是否有故障檢測和恢複的能力,例如告警、倒換等

使用開源項目的正确姿勢,都是血和淚的總結!

很多人用開源項目,其實是完完全全的“拿來主義”,看了幾個Demo,把程式跑起來就開始部署到線上應用了。就好像看了一下開車指南,知道了方向盤是轉向、油門是加速、刹車是減速,然後就開車上路了,其實是非常危險的。

案例:我們有團隊使用了elasticsearch,基本上是拿來就用,反向索引是什麼不太清楚,配置都是用預設值,跑起來就上線了,結果就遇到節點ping時間太長,剔除異常節點太慢,導緻整站通路挂掉。

案例2:很多團隊最初使用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、Facebook、Google......等)很多都是這樣做的,否則我們也就沒有那麼多好用的開源項目了 :)

原文釋出時間為:2018-04-10

本文作者:李運華

繼續閱讀