譯者 | 李睿
審校 | 重樓
微服務(wù)架構(gòu)已經(jīng)成為現(xiàn)代應(yīng)用程序開發(fā)的實際選擇。雖然它解決了許多問題,但并不是靈丹妙藥。像所有的軟件一樣,它也面臨一些需要解決的獨有挑戰(zhàn)。這就需要了解微服務(wù)中的常見設(shè)計模式,并使用可重用的解決方案來解決這些挑戰(zhàn)。
在深入研究設(shè)計模式之前,了解構(gòu)建微服務(wù)架構(gòu)的核心原則非常重要:
圖1微服務(wù)架構(gòu)核心實踐
應(yīng)用這些原則往往會帶來一些挑戰(zhàn)和問題。本文將提供關(guān)鍵微服務(wù)模式的細分、它們解決的常見問題以及它們提供的解決方案。
本文可以作為關(guān)鍵微服務(wù)模式和系統(tǒng)設(shè)計策略的參考,以有效地構(gòu)建微服務(wù)架構(gòu)。
分解設(shè)計模式
模式1:按業(yè)務(wù)能力分解
問題
微服務(wù)就是讓服務(wù)松散耦合,從而應(yīng)用單一責任原則。然而,將應(yīng)用程序分解為更小的部分必須符合邏輯。那么,如何將應(yīng)用程序分解為更小的服務(wù)?
解決方案
其中一種策略是按業(yè)務(wù)能力進行分解。業(yè)務(wù)能力是企業(yè)為了創(chuàng)造價值所做的事情,給定業(yè)務(wù)的能力取決于業(yè)務(wù)類型。例如,保險公司的能力通常包括銷售、營銷、承保、索賠處理、計費、合規(guī)等。每種業(yè)務(wù)能力都可以被視為一種服務(wù)——只不過它是面向業(yè)務(wù)的,而不是技術(shù)的。
模式2:按子域分解
問題
使用業(yè)務(wù)能力分解應(yīng)用程序可能是一個良好的開端,但是可能遇到所謂的“上帝類” (God Classes)問題,它們不容易分解。這些類在多個服務(wù)中是通用的。例如,訂單類將用于訂單管理、訂單接收、訂單交付等。那么,如何分解它們?
解決方案
針對“上帝類”問題,領(lǐng)域驅(qū)動設(shè)計(DDD)提供了解決方案。它使用子域和有界場景概念來解決這個問題。領(lǐng)域驅(qū)動設(shè)計(DDD)將為企業(yè)創(chuàng)建的整個域模型分解為子域。每個子域都有一個模型,該模型的范圍將被稱為有界場景。每個微服務(wù)都將圍繞有界場景進行開發(fā)。
注:識別子域并非易事,這需要對業(yè)務(wù)有所了解。就像識別業(yè)務(wù)能力一樣,子域是通過分析業(yè)務(wù)及其組織結(jié)構(gòu)并確定不同的專業(yè)領(lǐng)域來識別的。
模式3:扼殺者模式
問題
到目前為止所討論的設(shè)計模式都是針對綠地(Greenfield)分解應(yīng)用程序,但是80%的工作都是針對棕地(Brownfield)應(yīng)用程序,也就是大型的單片應(yīng)用程序。將之前討論的所有設(shè)計模式直接應(yīng)用于棕地應(yīng)用程序?qū)媾R巨大的挑戰(zhàn)——在保持其運行的同時,嘗試將其分解成更小的組成部分是一項極為艱巨的任務(wù)。
解決方案
扼殺者模式可以發(fā)揮重要作用。扼殺者模式基于藤蔓纏繞并扼殺樹木的類比。這一解決方案適用于來回調(diào)用的Web應(yīng)用程序,并且對于每個URI調(diào)用,可以將服務(wù)分解為不同的域并作為獨立的服務(wù)托管。這個想法是每次處理一個域。就在同一個URI空間中創(chuàng)建了兩個獨立的應(yīng)用程序。新重構(gòu)的應(yīng)用程序會“扼殺”或取代原來的應(yīng)用程序,直到最終可以關(guān)閉單片應(yīng)用程序。
集成模式
模式4:API網(wǎng)關(guān)模式
問題
當應(yīng)用程序被分解為更小的微服務(wù)時,有幾個問題需要解決:
1.如何調(diào)用抽象生產(chǎn)者信息的多個微服務(wù)。
2.在不同的渠道(例如臺式機電腦、筆記本電腦和平板電腦)上,應(yīng)用程序需要不同的數(shù)據(jù)來響應(yīng)相同的后端服務(wù),因為用戶界面(UI)可能不同。
3.不同的消費者可能需要來自可重用微服務(wù)的不同格式的響應(yīng)。誰將進行數(shù)據(jù)轉(zhuǎn)換或字段操作?
4.如何處理不同類型的協(xié)議——其中一些可能不被生產(chǎn)者微服務(wù)支持。
解決方案
API網(wǎng)關(guān)可以幫助解決微服務(wù)實現(xiàn)過程中出現(xiàn)的許多問題,但不限于上述問題:
1.API網(wǎng)關(guān)是任何微服務(wù)調(diào)用的單一入口點。
2.它可以作為代理服務(wù),將請求路由到相關(guān)的微服務(wù),抽象生產(chǎn)者的詳細信息。
3.它可以向多個服務(wù)發(fā)出請求,并將結(jié)果聚合并發(fā)送回消費者。
4.并沒有一個萬能的API能夠解決所有消費者的需求,這個解決方案可以為每種特定類型的客戶機創(chuàng)建細粒度的API。
5.它還可以將協(xié)議請求(例如AMQP)轉(zhuǎn)換為另一個協(xié)議(例如HTTP),反之亦然,以便生產(chǎn)者和消費者可以處理它。
6.它還可以減輕微服務(wù)的身份驗證/授權(quán)責任。
模式5:聚合器模式
問題
如上所述,在解決API網(wǎng)關(guān)模式中的聚合數(shù)據(jù)問題時通常面臨著挑戰(zhàn)。當將業(yè)務(wù)功能分解為幾個較小的邏輯代碼片段時,有必要考慮如何協(xié)作每個服務(wù)返回的數(shù)據(jù)。這個責任不能留給消費者,因為消費者可能需要理解生產(chǎn)者應(yīng)用程序的內(nèi)部實現(xiàn)。
解決方案
聚合器模式有助于解決這個問題。它討論了們?nèi)绾尉酆蟻碜圆煌?wù)的數(shù)據(jù),然后將最終響應(yīng)發(fā)送給消費者。這可以通過兩種方式實現(xiàn):
1.復(fù)合微服務(wù)將調(diào)用所有所需的微服務(wù),聚合數(shù)據(jù),并在發(fā)送回之前轉(zhuǎn)換數(shù)據(jù)。
2.API網(wǎng)關(guān)還可以將請求劃分為多個微服務(wù),并在將其發(fā)送給消費者之前聚合數(shù)據(jù)。
如果要應(yīng)用任何業(yè)務(wù)邏輯,建議選擇復(fù)合微服務(wù)。否則,API網(wǎng)關(guān)為已建立的解決方案。
模式6:客戶端用戶界面組合
問題
當通過分解業(yè)務(wù)功能/子域來開發(fā)服務(wù)時,負責用戶體驗的服務(wù)必須從幾個微服務(wù)中提取數(shù)據(jù)。在單片架構(gòu)中,過去只有一個從用戶界面(UI)到后端服務(wù)的調(diào)用來檢索所有數(shù)據(jù)和刷新/提交用戶界面(UI)頁面。然而,現(xiàn)在情況不同了,所以需要了解如何做到這一點。
解決方案
對于微服務(wù),用戶界面(UI)必須被設(shè)計成包含屏幕/頁面的多個部分/區(qū)域的框架。每個部分將調(diào)用一個單獨的后端微服務(wù)來提取數(shù)據(jù)。這被稱為組合特定于服務(wù)的用戶界面(UI)組件。像AngularJS和ReactJS這樣的框架可以很容易地做到這一點。這些屏幕被稱為單頁應(yīng)用程序(SPA)。這使得應(yīng)用程序可以刷新屏幕的特定區(qū)域,而不是整個頁面。
數(shù)據(jù)庫模式
模式7:每個服務(wù)使用的數(shù)據(jù)庫
問題
開發(fā)團隊經(jīng)常面臨如何為微服務(wù)定義數(shù)據(jù)庫架構(gòu)的挑戰(zhàn)。以下是必須解決的問題:
1.服務(wù)必須松散耦合。它們可以獨立開發(fā)、部署和擴展。
2.業(yè)務(wù)事務(wù)可以強制執(zhí)行跨多個服務(wù)的不變量。
3.一些業(yè)務(wù)事務(wù)需要查詢由多個服務(wù)擁有的數(shù)據(jù)。
4.數(shù)據(jù)庫有時必須復(fù)制和分片才能擴展。
5.不同的服務(wù)有不同的數(shù)據(jù)存儲需求。
解決方案
為了解決上述問題,必須為每個微服務(wù)設(shè)計一個數(shù)據(jù)庫。它必須僅對該服務(wù)私有,并且只能由微服務(wù)API訪問。其他服務(wù)無法直接訪問它。
例如,對于關(guān)系數(shù)據(jù)庫,可以使用“每個服務(wù)私有表”、“每個服務(wù)模式”或“每個服務(wù)數(shù)據(jù)庫服務(wù)器”。每個微服務(wù)都應(yīng)該有一個單獨的數(shù)據(jù)庫ID,這樣就可以提供單獨的訪問權(quán)限,從而建立一個屏障,防止它使用其他服務(wù)表。
圖2 每個服務(wù)的數(shù)據(jù)庫架構(gòu)
模式8:每個服務(wù)共享數(shù)據(jù)庫
問題
之前討論過,每個服務(wù)使用一個數(shù)據(jù)庫是微服務(wù)架構(gòu)的理想狀態(tài),但這只有在應(yīng)用是全新開發(fā)的,并且采用領(lǐng)域驅(qū)動設(shè)計(DDD)時才能實現(xiàn)。如果應(yīng)用程序是一個單體,并且試圖分解成微服務(wù),那么非規(guī)范化就不那么容易了。那么,在這種情況下,最合適的架構(gòu)是什么?
解決方案
每個服務(wù)共享數(shù)據(jù)庫并不理想,但這是針對以上場景的有效解決方案。大多數(shù)人認為這是微服務(wù)的反模式,但對于“棕地”應(yīng)用程序,這是可以將應(yīng)用程序分解為更小的邏輯組件的一個很好的起點。
在這種模式下,一個數(shù)據(jù)庫可以與多個微服務(wù)對齊,但最多只能對齊2~3個;否則,擴展、自主和獨立將難以執(zhí)行:
圖3共享數(shù)據(jù)庫架構(gòu)
模式9:命令查詢職責分離(CQRS)
問題
一旦實現(xiàn)了每個服務(wù)的數(shù)據(jù)庫,就需要查詢,這需要來自多個服務(wù)的聯(lián)合數(shù)據(jù)——這是不可能的。那么,如何在微服務(wù)架構(gòu)中實現(xiàn)查詢呢?
解決方案
命令查詢職責分離(CQRS)建議將應(yīng)用程序分成兩部分——命令端和查詢端。命令端處理創(chuàng)建、更新和刪除請求。查詢端通過使用物化視圖來處理查詢組件。事件溯源設(shè)計模式通常與它一起用于為任何數(shù)據(jù)更改創(chuàng)建事件。因此,物化視圖通過訂閱事件流來保持最新狀態(tài)。
模式10:Saga模式
問題
當每個服務(wù)都有自己的數(shù)據(jù)庫,并且業(yè)務(wù)事務(wù)跨越多個服務(wù)時,如何確??绶?wù)的數(shù)據(jù)一致性?例如,對于客戶有信用額度的電子商務(wù)應(yīng)用程序,應(yīng)用程序必須確保新訂單不會超過客戶的信用額度。由于訂單和客戶在不同的數(shù)據(jù)庫中,應(yīng)用程序不能簡單地使用本地ACID事務(wù)。
解決方案
Saga代表了一個由多個子請求組成的高級業(yè)務(wù)流程,每個子請求更新單個服務(wù)中的數(shù)據(jù)。每個請求都有一個補償請求,當請求失敗時執(zhí)行。它可以通過兩種方式實現(xiàn):
1.編排——當沒有中央?yún)f(xié)調(diào)機制時,每個服務(wù)都會產(chǎn)生并監(jiān)聽其他服務(wù)的事件,并決定是否需要采取行動。
2.編排——編排者(對象)負責一個Saga的決策和排序業(yè)務(wù)邏輯。
可觀察性模式
接下來深入了解微服務(wù)模式的可觀察性。以下是一個示例微服務(wù)架構(gòu)圖,供所有可觀察性主題參考。
圖4微服務(wù)可觀察性圖
模式11:日志聚合
問題
考慮一個用例,其中應(yīng)用程序由在多臺機器上運行的多個服務(wù)實例組成。請求通常跨越多個服務(wù)實例。每個服務(wù)實例都會生成一個標準化格式的日志文件。那么,如何通過特定請求的日志來理解應(yīng)用程序的行為?
解決方案
需要一個集中的日志服務(wù)來聚合來自每個服務(wù)實例的日志。用戶可以對日志進行查詢和分析。他們還可以配置警報,當日志中出現(xiàn)特定消息時觸發(fā)這些警報。例如,云平臺即服務(wù)(PCF)確實有Loggeregator,它從PCF平臺的每個組件(路由器、控制器、Diego等)以及應(yīng)用程序收集日志。AWS Cloud Watch也提供了類似的功能。
模式12:性能指標
問題
當服務(wù)組合由于微服務(wù)架構(gòu)而增加時,密切關(guān)注事務(wù)變得至關(guān)重要,以便在出現(xiàn)問題時可以監(jiān)控模式并發(fā)送警報。那么,應(yīng)該如何收集指標來監(jiān)控應(yīng)用程序性能?
解決方案
需要一個指標服務(wù)來收集有關(guān)單個操作的統(tǒng)計數(shù)據(jù)。該服務(wù)應(yīng)該聚合應(yīng)用程序服務(wù)的指標,以提供報告和警報功能。指標聚合有兩種模型:
- Push(推送)模型將指標推送到指標服務(wù),例如New Relic、AppDynamics等。
- Pull(拉?。┠P?/span>從指標服務(wù)中提取指標,例如Prometheus。
模式13:分布式跟蹤
問題
在微服務(wù)架構(gòu)中,請求通??缭蕉鄠€服務(wù)。每個服務(wù)通過跨多個服務(wù)執(zhí)行一個或多個操作來處理請求。那么,如何從端到端跟蹤請求以解決問題呢?
解決方案
需要這樣一項服務(wù):
- 為每個外部請求分配一個唯一的外部請求ID。
- 將外部請求ID傳遞給所有服務(wù)。
- 在所有日志消息中包含外部請求ID。
- 記錄在集中服務(wù)中處理外部請求時執(zhí)行的請求和操作的信息(例如,開始時間,結(jié)束時間等)。
Spring Cloud sluth和Zipkin server是一個常見的實現(xiàn)示例。
模式14:健康檢查
問題
當實現(xiàn)微服務(wù)架構(gòu)時,有可能出現(xiàn)服務(wù)啟動但無法處理事務(wù)的情況。在這種情況下,如何確保請求不會轉(zhuǎn)到那些失敗的實例?可以通過負載平衡模式實現(xiàn)來解決這個問題。
解決方案
每個服務(wù)都需要有一個端點,可用于檢查應(yīng)用程序的運行狀況,例如/health。這個API應(yīng)該檢查主機的狀態(tài)、與其他服務(wù)/基礎(chǔ)設(shè)施的連接以及任何特定的邏輯。
Spring Boot Actuator實現(xiàn)了/health端點,并且可以自定義實現(xiàn)。
橫切關(guān)注點模式
模式15:外部化配置
問題
服務(wù)通常也會調(diào)用其他服務(wù)和數(shù)據(jù)庫。對于dev、QA、UAT和/或prod等每個環(huán)境,端點URL或其他配置屬性可能不同。任何這些屬性的更改都可能需要重新構(gòu)建和重新部署服務(wù)。那么如何避免因配置更改而修改代碼?
解決方案
外部化配置(包括端點URL和憑據(jù))將緩解問題。應(yīng)用程序應(yīng)該在啟動時或運行時加載它們。
Spring Cloud配置服務(wù)器提供了將屬性外部化到GitHub并將其作為環(huán)境屬性加載的選項。這些可以在啟動時由應(yīng)用程序訪問,也可以在不重新啟動服務(wù)器的情況下刷新。
模式16:服務(wù)發(fā)現(xiàn)
問題
當微服務(wù)出現(xiàn)時,需要在調(diào)用服務(wù)方面解決一些問題:
1.通過容器技術(shù),IP地址可以動態(tài)地分配給服務(wù)實例。在每次地址更改時,消費者服務(wù)都可能中斷并需要人工更改。
2.消費者必須記住每個服務(wù)URL,并使其緊密耦合。
那么消費者或路由器如何知道所有可用的服務(wù)實例和位置呢?
解決方案
需要創(chuàng)建一個服務(wù)注冊表,它將記錄每個生產(chǎn)者服務(wù)的元數(shù)據(jù)。服務(wù)實例應(yīng)在啟動時向注冊表注冊,并在關(guān)閉時注銷。因此,消費者或路由器應(yīng)該查詢注冊表并找出服務(wù)的位置。
注冊表還需要對生產(chǎn)者服務(wù)進行健康檢查,以確保只有服務(wù)的工作實例可用并能夠通過它使用。有兩種類型的服務(wù)發(fā)現(xiàn):客戶端和服務(wù)器端。客戶端發(fā)現(xiàn)的一個示例是Netflix Eureka,服務(wù)器端發(fā)現(xiàn)的一個示例是AWS ALB。
模式17:斷路器(Circuit Breakers)
問題
服務(wù)通常會調(diào)用其他服務(wù)來檢索數(shù)據(jù),下游服務(wù)可能會宕機。這樣做有兩個問題:首先,請求將繼續(xù)向宕機的服務(wù)發(fā)送,耗盡網(wǎng)絡(luò)資源并降低性能。其次,用戶體驗將會很差且不可預(yù)測。那么,如何避免級聯(lián)服務(wù)故障并從容地處理故障?
解決方案
消費者應(yīng)該通過代理調(diào)用遠程服務(wù),該代理的行為方式類似于斷路器。當連續(xù)故障的數(shù)量超過閾值時,斷路器跳閘,并且在超時期間,所有調(diào)用遠程服務(wù)的嘗試都將立即失敗。在超時之后,斷路器允許有限數(shù)量的測試請求通過。如果這些請求成功,則斷路器恢復(fù)正常操作。否則,如果再次失敗,超時期將重新開始。
Netflix Hystrix是斷路器模式的良好實現(xiàn)。它還有助于定義一個回退機制,可以在斷路器跳閘時使用。這提供了更好的用戶體驗。
模式18:藍-綠部署
問題
使用微服務(wù)架構(gòu),一個應(yīng)用程序可以有許多微服務(wù)。如果停止所有服務(wù),然后部署增強版本,那么停機時間可能會很長,并影響業(yè)務(wù)。此外,任何回滾都將是一場噩夢。那么,如何避免或減少部署期間服務(wù)的停機時間呢?
解決方案
可以實現(xiàn)藍綠部署策略來減少或消除停機時間。它通過運行兩個相同的生產(chǎn)環(huán)境(藍色和綠色)來實現(xiàn)這一點。假設(shè)綠色是現(xiàn)有的活動實例,而藍色是應(yīng)用程序的新版本。在任何時候,只有一個環(huán)境是活動的,活動環(huán)境為所有生產(chǎn)流量服務(wù)。所有云平臺都提供了實現(xiàn)藍綠部署的選項。
結(jié)論
還有其他幾個關(guān)鍵的微服務(wù)架構(gòu)模式,例如sidecar模式、鏈式微服務(wù)、分支微服務(wù)、事件溯源設(shè)計模式、大使模式等等。隨著微服務(wù)架構(gòu)的不斷演進和發(fā)展,這個清單將會持續(xù)擴展。
原文標題:Microservices Design Patterns: Essential Architecture and Design Guide,作者:Rajesh Bhojwani