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