前言
網(wǎng)上看到一本關(guān)于微服務(wù)反模式的電子書,看后感覺內(nèi)容非常棒,于是我決定分階段翻譯成中文書,翻譯的目的也是想幫助想深入了解微服務(wù)的朋友,由于英文水平有限,如有翻譯不對(duì)之處希望多留言指正。
書籍英文目錄如下
書籍中文目錄如下:
1、數(shù)據(jù)驅(qū)動(dòng)的遷移反模式
1.1、太多的數(shù)據(jù)遷移
1.2、功能分割優(yōu)先,數(shù)據(jù)遷移最后
2、超時(shí)反模式
2.1、使用超時(shí)
2.2、使用斷路器模式
3、共享反模式
3.1、過多依賴
3.2、共享代碼的技術(shù)
4.到達(dá)報(bào)告反模式
4.1、微服務(wù)報(bào)告的問題
4.2、Asynchronous Event Pushing
5、沙粒陷阱
5.1、分析服務(wù)的范圍和功能
5.2、分析數(shù)據(jù)庫(kù)事務(wù)
5.3、分析服務(wù)編排
6、無(wú)因的開發(fā)者陷阱
7、隨大流陷阱
8、其它架構(gòu)模式
9、靜態(tài)契約陷阱
10、我們到了嗎陷阱
11、REST陷阱
一、數(shù)據(jù)驅(qū)動(dòng)的遷移反模式
微服務(wù)會(huì)創(chuàng)建大量小的、分布式的、單一用途的服務(wù),每個(gè)服務(wù)擁有自己的數(shù)據(jù)。這種服務(wù)和數(shù)據(jù)耦合支持一個(gè)有界的上下文和一個(gè)無(wú)共享數(shù)據(jù)的架構(gòu),其中,每個(gè)服務(wù)及其對(duì)應(yīng)的數(shù)據(jù)是獨(dú)立一塊,完全獨(dú)立于所有其他服務(wù)。服務(wù)只暴露了一個(gè)明確的接口(服務(wù)契約)。有界的上下文可以允許開發(fā)者以最小的依賴快速輕松地開發(fā),測(cè)試和部署。
采用數(shù)據(jù)驅(qū)動(dòng)遷移反模式主要發(fā)生在當(dāng)你從一個(gè)單體應(yīng)用向微服務(wù)架構(gòu)做遷移的時(shí)候。我們之所以稱之為反模式主要原因是,剛開始我們覺得創(chuàng)建微服務(wù)是一個(gè)不錯(cuò)的主意,服務(wù)和相應(yīng)的數(shù)據(jù)都獨(dú)立成微服務(wù),但這可能會(huì)將你帶向一個(gè)錯(cuò)誤的道路上,導(dǎo)致高風(fēng)險(xiǎn)、過剩成本和額外的遷移工作。
單體應(yīng)用遷移到微服務(wù)架構(gòu)有兩個(gè)主要目標(biāo):
下圖展示了一個(gè)典型的遷移,看起來(lái)像服務(wù)代碼和相應(yīng)的數(shù)據(jù)同時(shí)進(jìn)行遷移。
上圖中有三個(gè)服務(wù)是從單體應(yīng)用中劃分而來(lái),并且還劃分獨(dú)立的三個(gè)數(shù)據(jù)庫(kù),這是一個(gè)自然演變的過程,因?yàn)樵诿總€(gè)服務(wù)和數(shù)據(jù)庫(kù)之間都使用了最為關(guān)鍵的限界上下文,然而我們遇到的問題也正是基于這一過程將帶領(lǐng)我們進(jìn)入數(shù)據(jù)遷移的反模式。
1.1 太多的數(shù)據(jù)遷移
這種遷移路徑的主要問題是,我們很難在一次就能夠劃分清楚每個(gè)服務(wù)的粒度,從一個(gè)更粗粒度的服務(wù)開始著手,一步步的進(jìn)行細(xì)化工作,并且要多了解相關(guān)業(yè)務(wù)知識(shí),不斷的對(duì)服務(wù)的粒度進(jìn)行調(diào)整,我們來(lái)看圖1-1發(fā)現(xiàn)最左邊的服務(wù)粒度太粗了,需要再拆分成二個(gè)小的服務(wù),或者你發(fā)現(xiàn)左邊的二個(gè)服務(wù)粒度劃分的又太細(xì)了,需要進(jìn)行合并。而數(shù)據(jù)遷移要比源代碼遷移更復(fù)雜,更容易出錯(cuò),我們最好只為數(shù)據(jù)進(jìn)行一次遷移工作,因?yàn)閿?shù)據(jù)遷移是一個(gè)高風(fēng)險(xiǎn)的工作。
我們的微服務(wù)劃分也就是應(yīng)用代碼的遷移和數(shù)據(jù)的遷移。如圖1-2所示。
1.2 功能分割優(yōu)先,數(shù)據(jù)遷移最后
此模式主要采用的是一種避免的手段,以遷移服務(wù)的功能為第一,同時(shí)也需要注意服務(wù)和數(shù)據(jù)之間的限界上下文。我們可以通過合并與拆分的手段對(duì)服務(wù)進(jìn)行調(diào)整直到滿意為止,這時(shí)候就可以遷移數(shù)據(jù)了。
如圖1-3所示,左邊所有三個(gè)服務(wù)都已經(jīng)進(jìn)行了遷移和拆分,但是所有服務(wù)仍然使用的是同一個(gè)數(shù)據(jù)庫(kù),如果這是一個(gè)臨時(shí)中間方案還可以作為一個(gè)選擇,這時(shí)候我們就需要更多的了解服務(wù)如何使用,以及接受什么類型的請(qǐng)求數(shù)據(jù)等。
在圖1-3中,我們要注意最左邊的服務(wù)是如何發(fā)現(xiàn)粒度太粗而拆分成二個(gè)服務(wù)的。服務(wù)粒度最終確定完成之后,下一步就開始遷移數(shù)據(jù)了,采用這種方式可以避免重復(fù)的數(shù)據(jù)遷移。
二、超時(shí)反模式
微服務(wù)是一種分布式的架構(gòu),它所有的組件(也就是服務(wù))會(huì)被部署為單獨(dú)的應(yīng)用程序,并通過某種遠(yuǎn)程訪問協(xié)議進(jìn)行通訊。分布式應(yīng)用的挑戰(zhàn)之一就是如何管理遠(yuǎn)程服務(wù)的可用性和它們的響應(yīng)。雖然服務(wù)可用性和服務(wù)響應(yīng)都涉及到服務(wù)的通信,但它們是兩個(gè)完全不同的東西。服務(wù)可用性是服務(wù)消費(fèi)者連接服務(wù)并能夠發(fā)送請(qǐng)求的能力,服務(wù)響應(yīng)則關(guān)注服務(wù)的響應(yīng)時(shí)間。
如圖2-1的所示,如果此時(shí)服務(wù)消費(fèi)者無(wú)法連接到服務(wù)提供者的時(shí)候,通過會(huì)在毫秒級(jí)的時(shí)間里得到通知和反饋,這時(shí)候服務(wù)消費(fèi)者可以選擇是直接返回錯(cuò)誤信息還是進(jìn)行重試,但是如果服務(wù)提供者接收了請(qǐng)求卻不進(jìn)行響應(yīng)該怎么辦,在這種情況下服務(wù)消費(fèi)者可以選擇無(wú)限期等待或者設(shè)置超時(shí)時(shí)間,使用超時(shí)時(shí)間看起來(lái)是個(gè)好辦法,但是它會(huì)導(dǎo)致超時(shí)反模式。
2.1 使用超時(shí)
你可能感覺非常困惑,難道設(shè)置一個(gè)超時(shí)時(shí)間不是一件好事嗎?在大部分的情況下超時(shí)時(shí)間的錯(cuò)誤設(shè)置都會(huì)帶來(lái)問題。比如當(dāng)你上網(wǎng)購(gòu)物的時(shí)候,你提交了訂單,服務(wù)一直在處理沒有返回,你在超時(shí)的時(shí)候再提交訂單,顯然服務(wù)器需要更復(fù)雜的邏輯來(lái)處理重復(fù)提交訂單的問題。
那么超時(shí)時(shí)間設(shè)置多少合適呢?
在圖2-2中,通常的情況下平均響應(yīng)時(shí)間是2秒,在高并發(fā)的情況下最長(zhǎng)時(shí)間是5秒,因?yàn)榭梢允褂眉颖都夹g(shù)服務(wù)的超時(shí)時(shí)間設(shè)置為10秒。
圖2-2的解決方案似乎看起來(lái)很完美,它使每一個(gè)服務(wù)消費(fèi)者必須等待10秒,其實(shí)只是為了判斷服務(wù)沒有響應(yīng)。在大多數(shù)情況下,用戶在等待提交按鈕或放棄和關(guān)閉屏幕之前不會(huì)等待超過2到3秒。那就必須要有更好的辦法來(lái)解決。
2.2 使用斷路器模式
與上面超時(shí)的方法相比,使用斷路器的方式更為穩(wěn)妥,這種設(shè)計(jì)模式就像家里的電器的保險(xiǎn)絲一樣,當(dāng)負(fù)載過大,或者電路發(fā)生故障或異常時(shí),電流會(huì)不斷升高,為防止升高的電流有可能損壞電路中的某些重要器件或貴重器件,燒毀電路甚至造成火災(zāi)。保險(xiǎn)絲會(huì)在電流異常升高到一定的高度和熱度的時(shí)候,自身熔斷切斷電流,從而起到保護(hù)電路安全運(yùn)行的作用。
圖2-3說明了斷路器模式是如何工作的,當(dāng)服務(wù)保持響應(yīng)時(shí),斷路器將關(guān)閉,允許通過請(qǐng)求。如果遠(yuǎn)程服務(wù)突然變得不能響應(yīng),斷路器就會(huì)打開,從而阻止請(qǐng)求通過,直到服務(wù)再次響應(yīng)。當(dāng)然這并不像你家中的保險(xiǎn)絲,斷路器本身可以持續(xù)監(jiān)測(cè)服務(wù)。
斷路器模式相比設(shè)置超時(shí)的優(yōu)點(diǎn)是,使用者可以立即知道服務(wù)已變得不響應(yīng),而不必等待超時(shí),使用者將在毫秒內(nèi)服務(wù)不響應(yīng),而不是等待10秒獲得相同的信息。
另外斷路器可以通過幾種方式進(jìn)行監(jiān)控,最簡(jiǎn)單的方法是對(duì)遠(yuǎn)程服務(wù)進(jìn)行簡(jiǎn)單的心跳檢查,這種方式只是告訴斷路器服務(wù)是活的,但是要想獲取服務(wù)存活的詳細(xì)信息,就需要定期(比如10秒)獲取一次服務(wù)的詳細(xì)信息,還有一種方式是實(shí)時(shí)用戶監(jiān)控,這種方式可以動(dòng)態(tài)調(diào)整,一旦達(dá)到閾值,斷路器可以進(jìn)入半開放狀態(tài),可以設(shè)置一定數(shù)量的請(qǐng)求是通過(說1的10)。
三、共享反模式
微服務(wù)是一種無(wú)共享的架構(gòu),我更傾向于叫它為“盡量不共享”模式(share-as-little-as-possible), 因?yàn)榭傆幸恍┐a會(huì)在微服務(wù)之間共享。比如不提供一個(gè)身份驗(yàn)證的微服務(wù),而是將身份驗(yàn)證的代碼打包成一個(gè)jar文件:security.jar,其它服務(wù)都能使用。如果安全檢查是服務(wù)級(jí)別的功能,每個(gè)服務(wù)接收到請(qǐng)求都會(huì)檢查安全性,這種方式可以很好的提高性能。
然后如果太過頻繁的使用最終會(huì)出現(xiàn)依賴噩夢(mèng),如圖3-1所示,其中每個(gè)服務(wù)都依賴于多個(gè)自定義共享庫(kù)。
這種共享級(jí)別不僅破壞了每個(gè)服務(wù)的限界上下文,而且還引入了幾個(gè)問題,包括整體可靠性、更改控制、可測(cè)試性和部署能力。
3.1 過多依賴
在面向?qū)ο蟮能浖_發(fā)過程中,經(jīng)常會(huì)遇到共享的問題,特別是從單一分層結(jié)構(gòu)遷移到微服務(wù)結(jié)構(gòu)時(shí),圖3-2展示抽象類和共享,它們最終在多數(shù)單塊分層體系結(jié)構(gòu)中共享。
創(chuàng)建抽象類和接口是面向?qū)ο缶幊痰淖钪匾龇?,那我們?nèi)绾蝸?lái)處理數(shù)百個(gè)服務(wù)共享的代碼?
微服務(wù)架構(gòu)的主要目標(biāo)就是共享要盡可能的少,這有助于維護(hù)服務(wù)的限界上下文,使我們能夠快速的測(cè)試和布署。服務(wù)之間依賴越強(qiáng),服務(wù)隔離也就越困難,因此也就越難單獨(dú)進(jìn)行測(cè)試和布署。
3.2 共享代碼的技術(shù)
要避免這個(gè)反模式的最好辦法就是代碼不共享,但是實(shí)際工作中總會(huì)有一些代碼需要進(jìn)行共享,那這些共享代碼應(yīng)該放到哪里呢?
圖3-3給了四個(gè)最基本的技術(shù):
四、到達(dá)報(bào)告反模式
有四種方式可以處理微服務(wù)架構(gòu)中的報(bào)告。
前三種模式是從服務(wù)的數(shù)據(jù)庫(kù)中拉取數(shù)據(jù),所以這個(gè)反模式就叫'rearch-in reporting'。既然前三種會(huì)出現(xiàn)這中反模式,我們就先看看為什么它們會(huì)帶來(lái)麻煩。
4.1 微服務(wù)報(bào)告的問題
主要是二個(gè)方面的問題:
在微服務(wù)架構(gòu)體系中第一種是使用數(shù)據(jù)庫(kù)拉取模型,使用者直接從服務(wù)的數(shù)據(jù)庫(kù)拉取數(shù)據(jù),如圖4-1所示:
其實(shí)獲取數(shù)據(jù)最快、最容易的方法是直接訪問數(shù)據(jù)。雖然這在以前看似乎是個(gè)好主意,但它導(dǎo)致了服務(wù)之間的明顯依賴關(guān)系。而上圖會(huì)帶來(lái)數(shù)據(jù)庫(kù)的非獨(dú)立性。
避免數(shù)據(jù)的耦合的另一種技術(shù)稱為HTTP拉取模型。使用此模型不需要直接訪問每個(gè)服務(wù)的數(shù)據(jù)庫(kù),使用者只需要對(duì)每個(gè)服務(wù)發(fā)出一個(gè)REST HTTP調(diào)用就可以訪問其數(shù)據(jù)。如圖4-2所示。
這種方式的優(yōu)點(diǎn)是根據(jù)限界上下文劃分出了不同服務(wù),但是這種方式又太慢,無(wú)法滿足復(fù)雜的以及數(shù)據(jù)量較大數(shù)據(jù)獲取需求。
第三種是批量拉取模式,這種方式是獨(dú)立出一個(gè)報(bào)表數(shù)據(jù)庫(kù)或者數(shù)據(jù)倉(cāng)庫(kù),通過批處理作業(yè)將不同服務(wù)數(shù)據(jù)庫(kù)的數(shù)據(jù)拉取這個(gè)新獨(dú)立的數(shù)據(jù)庫(kù)中,如圖4-3所示。
這種模型的問題在于依然是強(qiáng)依賴數(shù)據(jù)庫(kù),如果拉取服務(wù)的數(shù)據(jù)庫(kù)進(jìn)行了更新,那么這個(gè)批量數(shù)據(jù)拉取過程也必將修改。
最后一種是異步事件模型,也是推薦使用的模型,如圖4-4所示
五、沙粒陷阱
架構(gòu)師和開發(fā)人員在采用微服務(wù)架構(gòu)的時(shí)候最大的挑戰(zhàn)之一就是服務(wù)粒度的問題。微服務(wù)的服務(wù)粒度多大合適?服務(wù)粒度至關(guān)重要,它會(huì)影響應(yīng)用的性能、健壯性、可靠性、可測(cè)性、設(shè)置發(fā)布模型。
當(dāng)服務(wù)的粒度太小的時(shí)候就會(huì)遇到沙粒陷阱。微服務(wù)的微并不意味著服務(wù)越小越好,但是多小是???
這種陷阱的主要原因之一是開發(fā)人員常常將服務(wù)與類混淆,往往一個(gè)類就是一個(gè)服務(wù),在這種情況下會(huì)很容易遇到沙粒陷阱。
服務(wù)應(yīng)該被看成是一個(gè)服務(wù)組件,服務(wù)組件應(yīng)該有一個(gè)清晰簡(jiǎn)明的角色和責(zé)任定義,并有一組明確的操作。由開發(fā)人員決定服務(wù)組件應(yīng)該如何實(shí)現(xiàn)以及服務(wù)需要多少個(gè)實(shí)現(xiàn)類。
如圖5-1所示,服務(wù)組件是通過一個(gè)或多個(gè)模塊的實(shí)現(xiàn)(比如,java類)。模型和服務(wù)組件如果是一對(duì)一的關(guān)系會(huì)使服務(wù)的粒度過細(xì)而后期難以維護(hù),而通過一個(gè)類實(shí)現(xiàn)的服務(wù)往往類太大,承擔(dān)太多的責(zé)任,也使它們難以維護(hù)和測(cè)試。
當(dāng)然微服務(wù)的粒度并不是靠服務(wù)實(shí)現(xiàn)的類的數(shù)量所決定的,有些服務(wù)很簡(jiǎn)單,只需一個(gè)簡(jiǎn)單的類就可以實(shí)現(xiàn),而有些確需要更多的類。既然類的數(shù)量不能用來(lái)決定微服務(wù)的粒度,那么用什么標(biāo)準(zhǔn)來(lái)衡量微服務(wù)的粒度是合適的呢?
主要有三種方式:
5.1 分析服務(wù)的范圍和功能
確定服務(wù)粒度級(jí)別是否正確的第一種方法是分析服務(wù)的范圍和功能。服務(wù)是做什么的?它的操作是什么?
比如一個(gè)顧客服務(wù)(customer service)有下面的操作:
在這個(gè)例子中前三個(gè)操作是相關(guān)的,它們都是用來(lái)管理和維護(hù)顧客信息的,但是后面三個(gè)并不是和CRUD操作相關(guān)的。在分析這個(gè)服務(wù)的完整性的時(shí)候,我們就比較清晰了,這個(gè)服務(wù)可以被分成三個(gè)服務(wù):顧客信息服務(wù)、顧客通知服務(wù)和顧客評(píng)論服務(wù)。
圖5-1 正是一種由粗粒度服務(wù)向細(xì)粒度服務(wù)逐步演進(jìn)的過程。
Sam Newman提供了一個(gè)很好的可操作的方法,開始不妨將服務(wù)劃分成粗粒度的服務(wù),隨著對(duì)服務(wù)了解更多,再進(jìn)一步劃分成更小粒度的服務(wù)。
5.2 分析數(shù)據(jù)庫(kù)事務(wù)
數(shù)據(jù)庫(kù)事務(wù)更正式的叫做 ACID 事務(wù) (atomicity, consistency, isolation, and durability)。ACID事務(wù)封裝多個(gè)數(shù)據(jù)庫(kù)更新為一個(gè)工作單元,工作單元要不整體完成,要不就出現(xiàn)錯(cuò)誤而回滾。
因?yàn)槲⒎?wù)架構(gòu)中服務(wù)是分布式的獨(dú)立的應(yīng)用,再兩個(gè)或者多個(gè)服務(wù)之間維護(hù) ACID 事務(wù)就極度困難,所以微服務(wù)架構(gòu)中經(jīng)常會(huì)依賴 BASE (basic availability, soft state, and eventual consistency)。盡管如此,你還是再特定的服務(wù)中要使用 ACID 事務(wù)。當(dāng)你需要在 ACID vs. BASE 事務(wù)中做艱難的決定的時(shí)候,可能你的服務(wù)劃分的就太細(xì)了。
當(dāng)發(fā)現(xiàn)不能使用最終一致性時(shí),你通常就會(huì)把服務(wù)從細(xì)粒度調(diào)整為粗粒度的服務(wù),如圖5-2所示。
5.3 分析服務(wù)編排
第三個(gè)衡量方式是分析服務(wù)編排。服務(wù)編排是指服務(wù)之間的通訊,通常也指內(nèi)部服務(wù)通訊。
遠(yuǎn)程調(diào)用服務(wù)是需要花時(shí)間的,它會(huì)降低應(yīng)用整體的性能。再者,它也會(huì)影響服務(wù)的健壯性和可靠性。
如果你發(fā)現(xiàn)完成一個(gè)邏輯請(qǐng)求需要調(diào)用太多的服務(wù)時(shí),服務(wù)的劃分可能粒度就太小了,對(duì)于單個(gè)業(yè)務(wù)請(qǐng)求,你調(diào)用的遠(yuǎn)程調(diào)用越多,其中一個(gè)遠(yuǎn)程調(diào)用失敗或超時(shí)的可能性就越大。
如果你發(fā)現(xiàn)需要與太多的服務(wù)進(jìn)行通信以完成單個(gè)業(yè)務(wù)請(qǐng)求,那么你的的服務(wù)可能粒度過細(xì)了。在分析服務(wù)編排水平,你通常會(huì)從細(xì)粒度的服務(wù)遷移到更粗,如圖5-4所示。
通過整合服務(wù)、合并到更粗粒度可以提升應(yīng)用的整體性能,提高應(yīng)用的健壯性和可靠性。你還可以移除服務(wù)之間的依賴,可以更好的控制、測(cè)試和發(fā)布。
當(dāng)然你可能會(huì)說調(diào)用多個(gè)服務(wù)可以并行的執(zhí)行,提高整體應(yīng)用的的響應(yīng)時(shí)間,比如 reactive 架構(gòu)的異步編程方式, 其實(shí)關(guān)鍵還是要權(quán)衡利弊, 確保對(duì)用戶的及時(shí)響應(yīng)以及系統(tǒng)整體的可靠性。
聯(lián)系客服