作者 | Tomasz Nurkiewicz
譯者 | 李騰輝
策劃 | 信遠(yuǎn)
微服務(wù)不能“包治百病”。
時(shí)下微服務(wù)是一個(gè)不錯(cuò)的架構(gòu),它具備模塊化、可伸縮和高容錯(cuò)這些優(yōu)點(diǎn)。許多公司都采用微服務(wù)架構(gòu)并取得了巨大的成功,自然而然地,如果你正開始一個(gè)新項(xiàng)目,微服務(wù)似乎是最佳選擇。
然而,大多數(shù)采用微服務(wù)取得成功的公司并不是一開始就選擇了這種架構(gòu)。以Airbnb和Twitter為例,他們?cè)趩误w應(yīng)用過于龐大之后才選擇了微服務(wù)路線,現(xiàn)在也仍在解決由此帶來的復(fù)雜性。即使是大公司也仍在尋找使用微服務(wù)的最佳方法。所以說,微服務(wù)是一把雙刃劍,需要權(quán)衡利弊。
從單體應(yīng)用遷移到微服務(wù)也絕不是一項(xiàng)簡(jiǎn)單任務(wù),未經(jīng)過測(cè)驗(yàn),便采用微服務(wù)構(gòu)建一個(gè)新產(chǎn)品則更加復(fù)雜。只有在充分評(píng)估了替代方案之后,才應(yīng)該認(rèn)真考慮是否使用微服務(wù)架構(gòu)。
一、 微服務(wù)僅適用于成熟產(chǎn)品
關(guān)于從頭開始使用微服務(wù),馬丁·福勒(Martin Fowler)總結(jié)道:
1.幾乎所有成功的微服務(wù)都是從一個(gè)過于龐大而不得不拆分的單體應(yīng)用開始的。
2.幾乎所有從頭開始以微服務(wù)構(gòu)建的系統(tǒng),最后都會(huì)因嚴(yán)重的問題而失敗。這種情況導(dǎo)致許多人認(rèn)為,就算你確信你的應(yīng)用將快速發(fā)展壯大,也不應(yīng)該一開始便采用微服務(wù)。
初版設(shè)計(jì)很難優(yōu)化得很好,新產(chǎn)品的前幾次迭代重點(diǎn)在于尋找用戶的真正痛點(diǎn)。因此,成功取決于保持敏捷并能快速優(yōu)化和重構(gòu)。在這方面,微服務(wù)就比單體應(yīng)用差得多。如果你沒有把握設(shè)計(jì)好最初的方案,就采用了微服務(wù),那么你的啟程之路將更加困難,因?yàn)橹貥?gòu)微服務(wù)比重構(gòu)單體應(yīng)用要困難得多。
二、你是否在初創(chuàng)公司或者開發(fā)全新項(xiàng)目?
作為一家初創(chuàng)公司,你已經(jīng)在爭(zhēng)分奪秒,在未知的噩耗來臨之前努力尋找突破口。此時(shí)你不太需要關(guān)注擴(kuò)展性(可能幾年之內(nèi)都不需要),那么為什么要使用復(fù)雜的架構(gòu)而忽視客戶的需求呢?
在開發(fā)全新項(xiàng)目時(shí)也有類似的情況,這些項(xiàng)目不受前期工作的限制,更沒有任何決策包袱。《構(gòu)建微服務(wù):設(shè)計(jì)細(xì)粒度的系統(tǒng)》一書的作者山姆·紐曼(Sam Newman)表示,用微服務(wù)構(gòu)建一個(gè)全新的項(xiàng)目非常困難:
我仍然堅(jiān)信,對(duì)現(xiàn)有的舊系統(tǒng)進(jìn)行劃分要比在全新的系統(tǒng)容易得多。你有更多可供幫助的資源,比如你有可供查閱的代碼,你可以與使用和維護(hù)系統(tǒng)的人員交流討論,你也知道一個(gè)“好”的系統(tǒng)是什么樣的——基于當(dāng)前穩(wěn)定運(yùn)作的系統(tǒng)進(jìn)行改變,讓你更容易知道你在哪里做錯(cuò)了,你的決策是否過于激進(jìn)。
三、微服務(wù)不是本地部署的最佳選擇
由于所有部件都是動(dòng)態(tài)變化的,微服務(wù)部署需要搭配更強(qiáng)大的自動(dòng)化機(jī)制。在常規(guī)環(huán)境下,我們可以依靠持續(xù)部署管道(continuous deployment pipelines)來完成工作——任務(wù)開發(fā)者部署微服務(wù),消費(fèi)端盡管使用線上服務(wù)就可以了。
然而這并不適用于本地環(huán)境,如果開發(fā)者發(fā)布一個(gè)包,需要消費(fèi)端自行在其本地環(huán)境上部署和配置其他的服務(wù),這使得部署變得更具挑戰(zhàn)性。
確切的說,開發(fā)本地微服務(wù)應(yīng)用也是可行的,正如Semaphore(一個(gè)CI/CD平臺(tái))也提供了本地化部署模式。然而,在這個(gè)過程中我們需要克服幾個(gè)挑戰(zhàn):
1.本地微服務(wù)的版本控制規(guī)則需要更加嚴(yán)格,你必須跟蹤參與發(fā)布的每個(gè)單獨(dú)的微服務(wù)。
2.你必須進(jìn)行完整的集成和端到端測(cè)試,因?yàn)槟銦o法在生產(chǎn)環(huán)境中進(jìn)行測(cè)試。
3.如果不能直接訪問生產(chǎn)環(huán)境,對(duì)微服務(wù)應(yīng)用進(jìn)行故障排查會(huì)困難得多。
四、你的單體應(yīng)用也許還能用
每個(gè)軟件都有自己的生命周期。你可能想廢棄一個(gè)單體應(yīng)用,因?yàn)樗芘f并且很復(fù)雜。但折騰一個(gè)系統(tǒng)也許費(fèi)力不討好,如果稍加努力,你也許可以榨出當(dāng)前系統(tǒng)的更多價(jià)值,讓他多用幾年。
只有在這兩種情況下,微服務(wù)重構(gòu)才是不得不做的選擇:
1.代碼混亂:在不破壞其他功能的情況下,很難在原代碼基礎(chǔ)上進(jìn)行更改和添加新功能
2.性能因素:你在擴(kuò)展單體應(yīng)用時(shí)遇到了瓶頸
五、模塊化單體
開發(fā)人員想要避免采用單體架構(gòu)的一個(gè)常見原因是,單體更容易變成一坨“代碼屎山”。那時(shí)很難再添加新功能,因?yàn)橐磺卸际窍嗷リP(guān)聯(lián)的。
但是單體不一定是一團(tuán)糟。以Shopify為例:他的代碼行數(shù)超過300萬行,是世界上最大的Rails單體應(yīng)用之一。但有一點(diǎn),系統(tǒng)過于龐大會(huì)給開發(fā)人員帶來許多痛苦:
應(yīng)用非常脆弱,新的代碼會(huì)產(chǎn)生許多意想不到的影響。作出一些更改可能會(huì)引發(fā)一連串無關(guān)的測(cè)試用例失敗。例如,計(jì)算運(yùn)費(fèi)和計(jì)算稅率復(fù)用了一些代碼,那么更改計(jì)算稅率代碼的同時(shí)可能會(huì)影響運(yùn)費(fèi)計(jì)算的結(jié)果。這是高耦合和缺乏邊界的結(jié)果,也導(dǎo)致測(cè)試用例難以編寫,并且在CI上運(yùn)行得非常緩慢。
Shopify沒有選擇將整個(gè)單體應(yīng)用重寫為微服務(wù),而是選擇了模塊化作為解決方案。
模塊化有助于設(shè)計(jì)更好的單體或者微服務(wù)。如果沒有認(rèn)真地定義好模塊,我們要么陷入傳統(tǒng)的分層式單體(大泥球),或者更差的結(jié)果,成了分布式單體應(yīng)用,它同時(shí)具備單體和微服務(wù)兩者的缺點(diǎn)。
模塊化的工作量很大,但它也帶來了巨大的價(jià)值,使開發(fā)可以更加直接。新開發(fā)人員在開始變更代碼之前不必了解整個(gè)應(yīng)用,一次只需要熟悉一個(gè)模塊。良好的模塊化可以使一個(gè)大單體更好上手。
模塊化是切換到微服務(wù)之前的必要步驟,并且有可能是更好的解決方案。與微服務(wù)類似,模塊化單體應(yīng)用通過將代碼拆分為一些獨(dú)立的模塊來解決代碼耦合的問題。與微服務(wù)通過網(wǎng)絡(luò)進(jìn)行通信不同,單體應(yīng)用中的模塊通過內(nèi)部API調(diào)用進(jìn)行通信。
分層式單體對(duì)比模塊化單體,模塊化單體具有微服務(wù)的許多特征,卻沒有微服務(wù)面臨的諸多挑戰(zhàn)。
六、單體應(yīng)用也能擴(kuò)展
另一個(gè)關(guān)于單體應(yīng)用的誤解是它們無法擴(kuò)展。如果你遇到性能問題并認(rèn)為微服務(wù)是唯一的出路,可以參考Shopify的案例,在音頻領(lǐng)域Shopify已經(jīng)在超大規(guī)模上構(gòu)建了一個(gè)可靠的單體應(yīng)用。
架構(gòu)和技術(shù)棧將決定如何優(yōu)化單體應(yīng)用,在做好模塊化劃分之后,可以利用云原生技術(shù)進(jìn)行擴(kuò)展:
1.部署單體應(yīng)用的多個(gè)實(shí)例,并使用負(fù)載均衡器來分配流量
2.使用CDN分發(fā)靜態(tài)資源和前端代碼
3.使用緩存來減少數(shù)據(jù)庫負(fù)載
4.使用邊緣計(jì)算(edge computing)或者無服務(wù)調(diào)用(serverless function)來實(shí)現(xiàn)高需求功能
七、如果系統(tǒng)可高效工作,不要輕易嘗試改變
如果我們將生產(chǎn)力衡量標(biāo)準(zhǔn)定義為每時(shí)間單位實(shí)現(xiàn)了多少個(gè)有價(jià)值的功能,那么在生產(chǎn)力值很高時(shí),切換架構(gòu)幾乎沒有意義。
由于維護(hù)開銷較大,微服務(wù)最初是生產(chǎn)力較低的架構(gòu),隨著單體的增長(zhǎng),系統(tǒng)變得更加復(fù)雜,并且更難添加新功能。微服務(wù)只有在交叉點(diǎn)之后才會(huì)獲得更高的生產(chǎn)力。
誠(chéng)然,有些事情最終還是要做,但那可能是幾年后才考慮的事。到那時(shí),需求可能已經(jīng)發(fā)生改變——誰知道那時(shí)候是否還會(huì)出現(xiàn)新的架構(gòu)模型呢?
八、布魯克斯定律和開發(fā)人員生產(chǎn)力
在《人月神話(The Mythical Man Month)》一書中,弗雷德里克·布魯克斯(Frederick P. Brooks, Jr.)曾說:“在軟件項(xiàng)目后期增加人力,會(huì)讓交付時(shí)間更晚”。發(fā)生這種事是因?yàn)楸仨毾葘?duì)新人員進(jìn)行指導(dǎo),然后才能在復(fù)雜的代碼上進(jìn)行開發(fā)。此外,隨著團(tuán)隊(duì)的壯大,溝通成本也會(huì)增加,使得組織決策更加困難。
在大型軟件開發(fā)時(shí),布魯克斯定律指出,在軟件項(xiàng)目后期增加人力只會(huì)讓花費(fèi)的時(shí)間更長(zhǎng)。微服務(wù)是減少定律影響的一種方法。然而,這種效果只能在復(fù)雜而龐大的代碼庫中才能體現(xiàn),因?yàn)樵谶@種情況下,我們無法將開發(fā)劃分為各自獨(dú)立的任務(wù)。
在使用微服務(wù)之前,你必須考慮布你的單體應(yīng)用是否正在被魯克斯定律所影響。過早地切換到微服務(wù)不會(huì)增加更多的價(jià)值。
九、你準(zhǔn)備好進(jìn)行切換了嗎?
在開始切換微服務(wù)之前,除了準(zhǔn)備好你的單體之外,你還必須滿足以下條件:
1.為自動(dòng)化部署設(shè)置好持續(xù)集成和持續(xù)部署(CI/CD)
2.實(shí)現(xiàn)快速配置以便按需構(gòu)建基礎(chǔ)架構(gòu)
3.了解云原生技術(shù)棧,包括容器化、K8S、無服務(wù)
4.熟悉領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD, Domain-Driven Design),測(cè)試驅(qū)動(dòng)開發(fā)(Test-Driven Development),行為驅(qū)動(dòng)開發(fā)(Behavior-Driven Development)
5.團(tuán)隊(duì)重組,以便跨職能溝通消除信息孤島,采用扁平化管理以激發(fā)創(chuàng)新
6.培養(yǎng)DevOps文化,使開發(fā)人員和運(yùn)維工作得更加契合
改變組織的文化可能需要數(shù)年時(shí)間,學(xué)習(xí)所有的必備知識(shí)也許需要數(shù)月時(shí)間,如果沒有做好準(zhǔn)備,切換到微服務(wù)是注定無法成功的。
十、總結(jié)
我們可以用一句話總結(jié)上面關(guān)于切換到微服務(wù)的討論:除非你有充分的理由,否則不要輕易去做。那些毫無準(zhǔn)備、沒有可靠設(shè)計(jì)就使用微服務(wù)的公司,都將經(jīng)歷一段非常艱難的時(shí)期。你需要建設(shè)好技術(shù)文化氛圍,做好技術(shù)儲(chǔ)備,再去考慮微服務(wù)。
同時(shí),如果你的系統(tǒng)運(yùn)行良好并且仍在以預(yù)期的速度進(jìn)行開發(fā),那么為什么要急于改變呢?
最后感謝你閱讀本文,祝你編碼愉快!
原文鏈接:
https://dzone.com/articles/when-microservices-are-a-bad-idea
譯者介紹
李騰輝,51CTO社區(qū)編輯,目前在一家東南亞互聯(lián)網(wǎng)金融獨(dú)角獸擔(dān)任資深Java工程師,負(fù)責(zé)金融借貸平臺(tái)架構(gòu)設(shè)計(jì)及核心建設(shè)工作,對(duì)互聯(lián)網(wǎng)金融架構(gòu)、微服務(wù)體系有較深入的研究,期望在互金領(lǐng)域持續(xù)深耕。