很多開發(fā)團(tuán)隊(duì)認(rèn)為,對(duì)于整體架構(gòu)來說,微服務(wù)架構(gòu)風(fēng)格是一種強(qiáng)有力的方式。但是其他團(tuán)隊(duì)認(rèn)為微服務(wù)架構(gòu)存在明顯生產(chǎn)率的負(fù)擔(dān)。和其它架構(gòu)風(fēng)格一樣,微服務(wù)也有利弊。為了做出最好的選擇,你必須理解這些并且使用到你特定的上下文。
微服務(wù)提供的好處:
1.強(qiáng)模塊邊界:微服務(wù)強(qiáng)化了模塊結(jié)構(gòu),對(duì)于大型團(tuán)隊(duì)尤其重要。
2.獨(dú)立部署:簡(jiǎn)單的服務(wù)更容易部署,并且因?yàn)樗鼈兪亲灾蔚?,?dāng)它們出現(xiàn)問題時(shí)極少可能導(dǎo)致系統(tǒng)故障。
3.技術(shù)多樣性:使用微服務(wù)你可以組合多種語言,開發(fā)框架和數(shù)據(jù)存儲(chǔ)技術(shù)。
但是微服務(wù)也有代價(jià):
1.分布:分布式系統(tǒng)更難組織,因?yàn)檫h(yuǎn)程調(diào)用慢并且總是有失敗風(fēng)險(xiǎn)。
2.最終一致性:對(duì)于分布式系統(tǒng)保持強(qiáng)一致性是極其困難的,這意味著每一個(gè)人必須管理最終一致性。
3.運(yùn)維復(fù)雜:你需要一個(gè)成熟的運(yùn)維團(tuán)隊(duì)來管理這么多經(jīng)常要被重新部署的服務(wù)。
強(qiáng)模塊邊界
微服務(wù)第一個(gè)大的好處是強(qiáng)模塊邊界。這是一個(gè)重要還不可思議的好處,因?yàn)槔碚撋蠜]有原因說明為什么一個(gè)微服務(wù)本應(yīng)該比一個(gè)整體有更強(qiáng)的模塊邊界。
那我說的強(qiáng)模塊邊界是什么呢?我想大多數(shù)人可能同意一個(gè)觀點(diǎn),把軟件劃分成多個(gè)模塊是好的:軟件的所有分塊都是相互解耦的。你要讓你的模塊工作已至于如果我需要改變系統(tǒng)中某個(gè)部分,大部分時(shí)間我只需要理解系統(tǒng)中的一小部分就可以修改,并且我發(fā)現(xiàn)那一小部分非常簡(jiǎn)單。好的模塊結(jié)構(gòu)在任何組織都是有用的,但是隨著軟件大小的成長(zhǎng)它的重要性會(huì)指數(shù)式增加??赡芨匾氖?,隨著開發(fā)團(tuán)隊(duì)的壯大,模塊結(jié)構(gòu)變的更加重要。
微服務(wù)的擁護(hù)者很快提出Conways Law,它的概念是軟件系統(tǒng)的結(jié)構(gòu)反映了構(gòu)建此軟件的組織的溝通結(jié)構(gòu)。對(duì)于更大的團(tuán)隊(duì),尤其是如果這些團(tuán)隊(duì)在不同的地點(diǎn),結(jié)構(gòu)化軟件并且認(rèn)識(shí)到團(tuán)隊(duì)之間交流將會(huì)比在一個(gè)團(tuán)隊(duì)內(nèi)的交流更少和更正式是非常重要的。在這種交流模式下微服務(wù)允許每個(gè)團(tuán)隊(duì)可以關(guān)心相對(duì)獨(dú)立的單元。
正如我早前說的,沒有原因說明為什么整體系統(tǒng)不應(yīng)有好的模塊結(jié)構(gòu)。但是很多人注意到這種情況似乎很少,因此 Big Ball of Mud是最普遍的架構(gòu)模式。事實(shí)上正是整體結(jié)構(gòu)的挫敗感驅(qū)使多個(gè)團(tuán)隊(duì)轉(zhuǎn)向微服務(wù)。解耦的模塊可以工作因?yàn)槟K邊界成為模塊間引用的屏障。問題就是,在整體系統(tǒng)下很容易偷偷溜進(jìn)邊界周圍。這樣做可能是快速實(shí)現(xiàn)功能的一個(gè)有用的技術(shù)捷徑,但是大范圍地使用破壞模塊結(jié)構(gòu),破壞團(tuán)隊(duì)的生產(chǎn)率。把模塊放進(jìn)獨(dú)立的服務(wù)使邊界更加堅(jiān)固,使使用這些不好的解決方法更加困難。
耦合的一個(gè)重要的方面是持久化數(shù)據(jù)。微服務(wù)的主要特征之一是去中心化數(shù)據(jù)管理,它是說每一個(gè)服務(wù)管理自己的數(shù)據(jù)庫,任何其它服務(wù)必須通過服務(wù)的API獲取數(shù)據(jù)。這消除了集成數(shù)據(jù)庫,在一個(gè)大系統(tǒng)中它是令人討厭的耦合的一個(gè)主要地方。
鄭重強(qiáng)調(diào)一下整體結(jié)構(gòu)非常有可能強(qiáng)化模塊邊界,但是它需要紀(jì)律。相似地你可能進(jìn)入一個(gè)微服務(wù)的大泥球,但是它需要很大精力做一些錯(cuò)事。以我看,使用微服務(wù)增加了你得到更好的模塊的可能性。如果你對(duì)你的團(tuán)隊(duì)的紀(jì)律有信心,這可能消除了微服務(wù)的這個(gè)優(yōu)勢(shì),但是隨著團(tuán)隊(duì)的成長(zhǎng),保持紀(jì)律漸漸地變的更加困難,正如維護(hù)模塊邊界變的更加重要一樣。
如果你得不到正確的邊界,這個(gè)優(yōu)勢(shì)會(huì)變成一個(gè)障礙。對(duì)于Monolith First策略,這是兩個(gè)主要原因之一,也是為什么那些早期傾向于運(yùn)轉(zhuǎn)微服務(wù)的人強(qiáng)調(diào)你只有在很好理解域的情況下使用微服務(wù)的兩個(gè)主要原因之一。
但是我仍然沒有在這一點(diǎn)上按照警告做。你可以僅僅完全地說明在一段時(shí)候過后一個(gè)系統(tǒng)是如何維護(hù)模塊的。所以當(dāng)我們看到一個(gè)已經(jīng)至少運(yùn)行一些年的微服務(wù)系統(tǒng),我們可以僅僅獲取是否微服務(wù)帶來了更好的模塊。此外,早期使用者變的更加有才華,所以在我們獲取一個(gè)由普通團(tuán)隊(duì)寫的微服務(wù)系統(tǒng)的優(yōu)勢(shì)時(shí)會(huì)有一些延遲。即使這樣,我們必須接受普通團(tuán)隊(duì)寫的普通軟件,而不是和頂級(jí)團(tuán)隊(duì)比較。我們必須與整體架構(gòu)下的寫的軟件比較-這是一個(gè)巧妙的事實(shí)統(tǒng)計(jì)。
此時(shí)此刻,接下來是從我認(rèn)識(shí)的使用這種風(fēng)格的人那聽到的早期證據(jù)。他們的意見是對(duì)于維護(hù)他們的模塊,這種風(fēng)格相當(dāng)簡(jiǎn)單。
一個(gè)案例研究特別有意思。這個(gè)團(tuán)隊(duì)做了一個(gè)錯(cuò)誤的決定,在一個(gè)不足夠復(fù)雜到包含Microservice Premium的系統(tǒng)上使用微服務(wù)。項(xiàng)目遇到了麻煩需要營(yíng)救,所以更多的人投入到這個(gè)項(xiàng)目。此刻微服務(wù)架構(gòu)變的有幫助,因?yàn)橄到y(tǒng)能夠承受開發(fā)人員的快速涌入,團(tuán)隊(duì)能夠更容易的利用更多團(tuán)隊(duì)成員相對(duì)于團(tuán)隊(duì)在整塊系統(tǒng)的情況下。結(jié)果項(xiàng)目加快了比在整塊系統(tǒng)下期望的更多的生產(chǎn)率,使團(tuán)隊(duì)趕上。結(jié)果仍然有一些凈負(fù)數(shù)的,軟件花費(fèi)的員工工時(shí)大于在整體架構(gòu)下花費(fèi)的時(shí)間,但是微服務(wù)架構(gòu)支持極速增加。
分布
微服務(wù)使用分布式系統(tǒng)來提高模塊化。但是分布式軟件有一個(gè)主要的劣勢(shì),也正是它的分布式。一旦你要打分布式牌,你要遭受一大堆復(fù)雜性。我不認(rèn)為微服務(wù)社區(qū)和分布式對(duì)象活動(dòng)一樣關(guān)于它們的開銷是沒有經(jīng)驗(yàn)的,但是復(fù)雜性仍然存在。
這些復(fù)雜性中的一個(gè)是性能。很多天你必須在一個(gè)真正不尋常的點(diǎn)上觀察進(jìn)程內(nèi)函數(shù)調(diào)用變成一個(gè)性能瓶頸,但是遠(yuǎn)程調(diào)用慢。如果你的服務(wù)調(diào)用了好幾次遠(yuǎn)程服務(wù),每一個(gè)遠(yuǎn)程服務(wù)又好幾次調(diào)用其它遠(yuǎn)程服務(wù),這些響應(yīng)時(shí)間意味著一些糟糕的延遲特征。
當(dāng)然你可以做一些事情減輕這種問題。首先你可以增加你的調(diào)用粒度,以至于你使用更少的調(diào)用。這使你的編碼模型復(fù)雜化,現(xiàn)在你必須思考如何批處理你的內(nèi)部服務(wù)間交互。它也只能帶你到這,因?yàn)槟銓⒁坏貌恢辽僬{(diào)用每一個(gè)合作服務(wù)一次。
第二種減輕方法是使用異步。如果并發(fā)的使用6個(gè)異步,你會(huì)和最慢的調(diào)用一樣慢而不是它們延遲的和。這是一個(gè)大的性能優(yōu)化,但是它帶來另一個(gè)認(rèn)知成本。異步編程是很難的:很難寫對(duì),同時(shí)更難調(diào)試。但是我聽到的大多數(shù)微服務(wù)例子使用異步為了獲得可接受的性能。
速度可靠之后,你期望進(jìn)程內(nèi)函數(shù)調(diào)用能夠工作,但是一個(gè)遠(yuǎn)程調(diào)用可能在任何時(shí)候失敗。隨著更多微服務(wù),會(huì)有更多潛在的失敗點(diǎn)。聰明的開發(fā)人員知道這個(gè)并且為失敗做設(shè)計(jì)。幸運(yùn)地是為異步合作采取的各種措施也適用于處理失敗的情況并且結(jié)果能夠提高彈性。不是太多的補(bǔ)償,但是,你仍然有為每一個(gè)遠(yuǎn)程調(diào)用執(zhí)行失敗結(jié)論的復(fù)雜性事情。
這僅僅是分布式計(jì)算的前兩個(gè)謬論。
對(duì)這個(gè)問題有一些警告。首先很多這些問題隨著整體架構(gòu)突然發(fā)生當(dāng)它發(fā)展的時(shí)候。很少的整體架構(gòu)是真正自包含的,常常有其它的系統(tǒng),常常是遺留系統(tǒng)需要一同工作。與它們交互涉及到通過網(wǎng)路同時(shí)引用了這些相同的問題。這既是為什么很多人傾向于更快地轉(zhuǎn)移到微服務(wù)來處理與遠(yuǎn)程系統(tǒng)同交互。這種問題也是一個(gè)經(jīng)驗(yàn)?zāi)軌驇兔Φ牡胤?,一個(gè)更加熟練的團(tuán)隊(duì)將更好的處理這種分布式問題。
但是分布式一直是一個(gè)成本。我一直勉強(qiáng)使用分布式,并且認(rèn)為很多人很快使用分布式是因?yàn)樗麄兊凸懒怂膯栴}。
最終一致性
我確信你知道網(wǎng)站需要一點(diǎn)耐心。你更新一些內(nèi)容,你的屏幕被刷新同時(shí)更新的內(nèi)容沒有了。你等了一兩分鐘,點(diǎn)擊刷新,它出現(xiàn)了。
這是一個(gè)非常令人不快的可用性問題,幾乎確定是由于最終一致性危險(xiǎn)造成的。你的更新被粉紅色節(jié)點(diǎn)接收,但是你的獲取請(qǐng)求被綠色節(jié)點(diǎn)處理。直到綠色節(jié)點(diǎn)從粉色節(jié)點(diǎn)得到它的更新,否則你是處在一個(gè)不一致的窗口。最終它將會(huì)一致,但是直到最終一致時(shí),你一直疑惑是否那里出錯(cuò)了。
像這種不一致情況特別令人不快,但是它們可能是非常重要的。業(yè)務(wù)邏輯可能根據(jù)不一致信息作出決定而結(jié)束,當(dāng)這種情況發(fā)生時(shí),診斷是哪里出錯(cuò)將會(huì)極為困難,因?yàn)槿魏握{(diào)查都是在一致窗口已經(jīng)關(guān)閉很長(zhǎng)時(shí)間之后發(fā)生。
微服務(wù)引入最終一致性內(nèi)容由于它們值得贊賞的主張關(guān)于去中心化數(shù)據(jù)管理。使用整體架構(gòu),你可以在一個(gè)事務(wù)里更新一長(zhǎng)串內(nèi)容。微服務(wù)需要多個(gè)資源去更新,同時(shí)分布式事物很頭疼。所以現(xiàn)在,開發(fā)人員需要注意一致性問題,并且搞清楚當(dāng)數(shù)據(jù)不同步時(shí)在代碼將要做任何回退之前如何檢測(cè)。
整體架構(gòu)的世界對(duì)于這些問題也不自由。隨著系統(tǒng)的發(fā)展,有更多的需求需要使用緩存去提高性能,緩存失效是另一個(gè)問題。大部分應(yīng)用使用離線鎖來避免長(zhǎng)鏈接的數(shù)據(jù)庫事務(wù)。外部系統(tǒng)需要更新,這些更新不能與一個(gè)事務(wù)管理器協(xié)作。業(yè)務(wù)處理常常對(duì)不一致的容忍度比你認(rèn)為的要強(qiáng),因?yàn)闃I(yè)務(wù)常常更珍視使用性(業(yè)務(wù)過程有一種CAP定理的自然理解)。
和其它分布式問題一樣,整體架構(gòu)不能完全避免一致性問題,但是它們?cè)馐苓@種問題更少,尤其是當(dāng)它們很小的時(shí)候。
獨(dú)立部署
對(duì)模塊邊界和分布式系統(tǒng)的復(fù)雜性的權(quán)衡充滿了我在這個(gè)領(lǐng)域的整個(gè)職業(yè)生涯。但是在過去的10年有一件事情明顯改變了,就是發(fā)布到產(chǎn)線的角色。在20世紀(jì)產(chǎn)品發(fā)布幾乎是一個(gè)痛苦又極少的事情,在周末的白天或者晚上將軟件有問題的部分切換到做有用事情的地方。但是現(xiàn)在,熟練的團(tuán)隊(duì)頻繁的發(fā)布到產(chǎn)線,很多組織實(shí)踐持續(xù)交付,運(yùn)行他們一天內(nèi)做多次發(fā)布。
這種轉(zhuǎn)變已經(jīng)對(duì)軟件工業(yè)有了深遠(yuǎn)影響,它與微服務(wù)活動(dòng)深深的纏繞在一起。多個(gè)微服務(wù)的成就都是被部署大的整塊架構(gòu)應(yīng)用的難題觸發(fā)的,在整體架構(gòu)中一個(gè)小的改變都可能引起整個(gè)部署失敗。微服務(wù)的一個(gè)主要原則是服務(wù)是組件,可以獨(dú)立部署的。所以現(xiàn)在當(dāng)你做了一個(gè)改變,你僅僅需要測(cè)試和部署一個(gè)小的服務(wù)。如果你把它弄亂了,你不用將整個(gè)系統(tǒng)停掉。畢竟,由于對(duì)失敗設(shè)計(jì)的需要,即使你的組件完全失敗也不應(yīng)該阻止系統(tǒng)的其它部分工作,即使有一些優(yōu)雅的降級(jí)處理。
這種關(guān)系是一種雙向道。隨著很多微服務(wù)需要頻繁發(fā)布,你的部署要一起進(jìn)行是基本要求。這就是為什么快速應(yīng)用部署和基礎(chǔ)的快速規(guī)定是微服務(wù)的前提條件。對(duì)于超出基本要求的任何事情,你需要進(jìn)行持續(xù)交付。
持續(xù)交付的很大好處是從想法到可運(yùn)行軟件的周期減少了。這樣做的阻止能夠快速應(yīng)對(duì)市場(chǎng)變化,比他們的競(jìng)爭(zhēng)對(duì)手更快地引用新功能。
盡管很多人引用持續(xù)交付作為使用微服務(wù)的一個(gè)原因,但是本質(zhì)上一個(gè)大型的整體系統(tǒng)也能做到持續(xù)交付。Facebook和Etsy就是兩個(gè)最有名的例子。也有足夠多的嘗試微服務(wù)架構(gòu)做獨(dú)立部署失敗的例子,多個(gè)服務(wù)需要小心合作發(fā)布。當(dāng)我聽到很多人爭(zhēng)論使用微服務(wù)更容易進(jìn)行持續(xù)交付,比起他們對(duì)于模塊化的實(shí)踐重要性,我更不堅(jiān)信他們?cè)谖⒎?wù)上的看法-盡管模塊化天然地與交付速度有很強(qiáng)的關(guān)聯(lián)。
運(yùn)維復(fù)雜化
能夠快速部署一個(gè)小的獨(dú)立的單元對(duì)于部署來說是極好的,但是隨著5,6個(gè)應(yīng)用轉(zhuǎn)成上百個(gè)小的微服務(wù),它把額外的負(fù)擔(dān)放到了運(yùn)維上。很多組織發(fā)現(xiàn)處理如此多的快速改變的工具的困難代價(jià)很高。
這強(qiáng)化了持續(xù)交付的重要作用。雖然持續(xù)交付對(duì)整體架構(gòu)是一個(gè)可用的技能,幾乎一直要?jiǎng)澐志Σ拍艿玫剑瑢?duì)于一系列微服務(wù)設(shè)置它是一種基本要求。沒有持續(xù)交付促進(jìn)的自動(dòng)化和合作,就沒有辦法處理10多個(gè)服務(wù)。由于對(duì)管理和監(jiān)控這些服務(wù)的需求增加,運(yùn)維復(fù)雜性也增加了。此外對(duì)于整體應(yīng)用有用的成熟度級(jí)別,如果微服務(wù)在混合場(chǎng)景下,成熟度級(jí)別也是必須的。
處理這種運(yùn)維復(fù)雜度需要一大堆新的技能和工具-特別強(qiáng)調(diào)在技能上。工具仍然不成熟,但是我的直覺告訴我即使使用更好的工具,在微服務(wù)環(huán)境下對(duì)技能的低障礙也是更高的。
到目前為止對(duì)于更好的技能和工具的需求不是處理這些運(yùn)維復(fù)雜性的最難的部分。為了高效的做所有事情你也需要引入devops 文化:開發(fā)人員,運(yùn)維和其它涉及到軟件交付的人之間更好合作。文化的改變是困難的,尤其在更大,更久的組織。如果你不能彌補(bǔ)技能和文化的改變,你的整體應(yīng)用將會(huì)被阻礙,但是你的微服務(wù)應(yīng)用也會(huì)痛苦。
技術(shù)多樣性
因?yàn)槊恳粋€(gè)微服務(wù)是獨(dú)立的部署單元,在每個(gè)微服務(wù)下你可以自由考慮你的技術(shù)選擇。微服務(wù)可以使用不同的語言,不能的庫和不同的數(shù)據(jù)存儲(chǔ)器。這允許團(tuán)隊(duì)對(duì)于任務(wù)選擇合適的工具,某個(gè)語言和庫對(duì)一個(gè)特定類型的問題更合適。
技術(shù)多樣性的討論常常集中在對(duì)工作最好的工具上,但是微服務(wù)最大的好處是更普通的版本問題。在整體架構(gòu)下你可能僅僅使用一個(gè)庫,一個(gè)地方的一個(gè)版本,這常常導(dǎo)致升級(jí)問題。系統(tǒng)的某個(gè)部分需要升級(jí)從而使用新功能但是不可以因?yàn)樯?jí)會(huì)打斷系統(tǒng)的其它部分。處理版本問題是這些問題中的一個(gè),隨著代碼變大,這些問題會(huì)以指數(shù)級(jí)變得更難。
使用多種技術(shù)有風(fēng)險(xiǎn),開發(fā)組織將會(huì)不知所措。我知道的大多數(shù)組織鼓勵(lì)有限的技術(shù)集合。這種鼓勵(lì)通過為諸如監(jiān)控等提供基本的工具來支持,這使服務(wù)附著于一個(gè)公共環(huán)境的小文件上更加簡(jiǎn)單。
不要低估支持實(shí)驗(yàn)的價(jià)值。對(duì)于一個(gè)整體架構(gòu)系統(tǒng),早期對(duì)語言和框架的決定是很難被顛覆的。10年后或許這個(gè)決定把團(tuán)隊(duì)鎖定到一個(gè)不方便的技術(shù)上。微服務(wù)允許團(tuán)隊(duì)實(shí)踐新工具,漸漸地遷移系統(tǒng)一次一個(gè)服務(wù)。一個(gè)更強(qiáng)的技術(shù)變得非常明確。