微服務(wù)架構(gòu)和微服務(wù)的設(shè)計(jì)模式
微服務(wù)在企業(yè)中可以帶來積極的影響。 因此,如何處理微服務(wù)體系架構(gòu)(MSA)和一些微服務(wù)設(shè)計(jì)模式以及微服務(wù)體系架構(gòu)的一般目標(biāo)或原則是很有必要的。 以下是微服務(wù)架構(gòu)實(shí)現(xiàn)中要考慮的四個(gè)目標(biāo)
- 降低成本 — MSA將降低設(shè)計(jì),實(shí)施和維護(hù)IT服務(wù)的整體成本
- 提高發(fā)布速度 — MSA將提高項(xiàng)目從構(gòu)建到部署的速度。
- 提升彈性 — MSA將提升我們服務(wù)網(wǎng)絡(luò)的彈性
- 有可見性 — MSA為您的服務(wù)和網(wǎng)絡(luò)上提供更好的可見性。
MSA是建立在哪些原則基礎(chǔ)之上是需要你去了解的
- 可擴(kuò)展性
- 可用性
- 彈性擴(kuò)展
- 靈活性
- 獨(dú)立性,自主性
- 去中心化治理
- 故障隔離
- 自動配置
- 通過DevOps持續(xù)交付
在堅(jiān)持這些原則基礎(chǔ)上推廣自己的解決方案或者系統(tǒng)會帶來一些挑戰(zhàn)和問題,這些問題在許多解決方案中都很常見,而且可以通過使用正確的設(shè)計(jì)模式來解決, 這些就是微服務(wù)的設(shè)計(jì)模式,這些模式可以分為五個(gè)大類,而每一類又包含了許多設(shè)計(jì)模式。具體如下圖所示:
解耦模式(Decomposition Patterns)
按業(yè)務(wù)能力解耦
通過運(yùn)用單一職責(zé)原則,微服務(wù)總是會把服務(wù)之間的耦合變?yōu)樗神詈?,微服?wù)通過業(yè)務(wù)能力解耦,而且服務(wù)的定義是對應(yīng)于業(yè)務(wù)能力。業(yè)務(wù)能力這個(gè)概念來自于業(yè)務(wù)架構(gòu)模型,某種程度上來說,業(yè)務(wù)確實(shí)是可以產(chǎn)生價(jià)值,業(yè)務(wù)能力經(jīng)常是對應(yīng)于一個(gè)業(yè)務(wù)實(shí)體對象。例如:
- 訂單管理對應(yīng)于訂單
- 客戶管理對應(yīng)于客戶
按照子域解耦
按照業(yè)務(wù)能力解耦一個(gè)應(yīng)用可能是一個(gè)好的開始,但是你可能會遇到所謂的“神類”(God Classes),就是哪些不容易解耦的類,而且這些類在多個(gè)服務(wù)之間很常見。領(lǐng)域驅(qū)動設(shè)計(jì)(DDD) 參考應(yīng)用問題空間--業(yè)務(wù)--做為一個(gè)域(domain)。一個(gè)域由多個(gè)子域組成,而每一個(gè)子域?qū)?yīng)于業(yè)務(wù)的不同的部分。
子域可以分類如下:
- 核心(Core) — 區(qū)分業(yè)務(wù)的關(guān)鍵 和 應(yīng)用中最有價(jià)值的部分
- 支撐(Supporting?) — 與業(yè)務(wù)相關(guān),但是不是關(guān)鍵部分,可以內(nèi)部實(shí)現(xiàn),也可以外部實(shí)現(xiàn)
- 泛化(Generic?) — 不針對特定的業(yè)務(wù),理想情況下使用現(xiàn)成的軟件實(shí)施
訂單管理的子域包括:
- 產(chǎn)品目錄服務(wù)
- 庫存管理服務(wù)
- 訂單管理服務(wù)
- 交付管理服務(wù)
按事務(wù)解耦/兩階段提交(2PC)模式
可以通過事務(wù)分解服務(wù),然后系統(tǒng)中將會有多個(gè)事務(wù)。 分布式事務(wù)處理的重要參與者之一是事務(wù)處理協(xié)調(diào)器[3]。分布式事務(wù)包括兩個(gè)步驟:
- 準(zhǔn)備階段 — 在此階段中,事務(wù)的所有參與者都準(zhǔn)備提交并通知協(xié)調(diào)器他們已準(zhǔn)備好完成事務(wù)。
- 提交或者回滾階段 — 在此階段中,事務(wù)協(xié)調(diào)器向所有參與者發(fā)出提交或回滾命令
2PC的問題是和單個(gè)微服務(wù)執(zhí)行時(shí)間來對比耗時(shí)長。即使微服務(wù)在相同的網(wǎng)段中,協(xié)調(diào)微服務(wù)之間的事務(wù)依舊會拖慢整個(gè)系統(tǒng)。因此這個(gè)解決方案一般不使用在高負(fù)載的場景中
扼殺模式
以上三種設(shè)計(jì)模式用于對未開發(fā)的應(yīng)用(greenfield apps)的解耦, 但是我們80%的工作都是和龐大而僵化的應(yīng)用(遺留代碼庫)打交道。扼殺模式(Strangler Pattern)就是為了解決這個(gè)問題而來的。在相同的URI空間中創(chuàng)建兩個(gè)獨(dú)立共存的應(yīng)用,隨著時(shí)間的推移,重構(gòu)過的新應(yīng)用將“扼殺”或者替代原來的應(yīng)用,直到最終把龐大而僵化的應(yīng)用關(guān)閉掉。扼殺應(yīng)用(Strangler Application)的步驟分為轉(zhuǎn)換,共存和消滅三步[4]:
- 轉(zhuǎn)換(Transform?) —? 用現(xiàn)代方式創(chuàng)建一個(gè)新的平行的站點(diǎn)
- 共存(Coexist?) —? 將已有的站點(diǎn)重定向到新的站點(diǎn),新站點(diǎn)逐步實(shí)現(xiàn)老站點(diǎn)的功能
- 消滅(Eliminate?) —? 移除已有的站點(diǎn)的舊的功能
隔板模式
將應(yīng)用程序的元素隔離到池中,以便如果其中一個(gè)失敗,其他應(yīng)用程序?qū)⒗^續(xù)運(yùn)行提供服務(wù),這個(gè)設(shè)計(jì)模式稱為隔板模式(Bulkhead), 因?yàn)樗愃朴诖w中一個(gè)個(gè)被隔離的分區(qū)。根據(jù)使用者負(fù)載和可用性要求,這些分區(qū)服務(wù)實(shí)例被分割到不同的組里面。這種設(shè)計(jì)模式有助于隔離故障(isolate failures), 并允許即使在故障期間仍可為某些使用者維持服務(wù)功能.
數(shù)據(jù)庫模式(Database Patterns)
為微服務(wù)定義數(shù)據(jù)庫架構(gòu)時(shí),我們需要考慮以下幾點(diǎn):
- 服務(wù)之間必須是松散耦合的, 它們可以獨(dú)立開發(fā),部署和擴(kuò)展。
- 業(yè)務(wù)事務(wù)在跨越多個(gè)微服務(wù)的時(shí)候保證不變
- 一些業(yè)務(wù)事務(wù)跨越多個(gè)微服務(wù)來查詢數(shù)據(jù)
- 有時(shí)數(shù)據(jù)庫必須可以復(fù)制,并且可以彈性共享
- 不同的服務(wù)有不同的數(shù)據(jù)存儲要求
每一個(gè)服務(wù)對應(yīng)一個(gè)數(shù)據(jù)庫(Database per Service)
為了解決上述問題,必須為每個(gè)微服務(wù)設(shè)計(jì)一個(gè)數(shù)據(jù)庫. 該數(shù)據(jù)庫只能是該服務(wù)私有的,并且只能通過微服務(wù)的API訪問,不能被其他的微服務(wù)直接訪問。例如,對關(guān)系型數(shù)據(jù)庫,我們可以使用 每個(gè)服務(wù)有私有化的表(private-tables-per-service), 每個(gè)服務(wù)有自己的schema (schema-per-service), 或者每個(gè)服務(wù)有私有的數(shù)據(jù)庫服務(wù)器 (database-server-per-service)
每一個(gè)服務(wù)共享數(shù)據(jù)庫 (Shared Database per Service)
我們已經(jīng)討論了每個(gè)服務(wù)一個(gè)數(shù)據(jù)庫是微服務(wù)的理想選擇,但它是微服務(wù)的反模式(anti-pattern)。如果一個(gè)單一而又龐大的應(yīng)用,并試圖把它拆分為微服務(wù),那么數(shù)據(jù)庫的反范式化(denormalization )就不那么容易。將每個(gè)微服務(wù)共享數(shù)據(jù)庫不是理想的情況,但是是可行的解決方案。大多數(shù)人認(rèn)為這是微服務(wù)的反模式,但對于brownfield 應(yīng)用,這是將應(yīng)用程序分解成較小邏輯部分的一個(gè)很好的開始。但是對于greenfield 應(yīng)用不太適用。
命令查詢的責(zé)任分離 (Command Query Responsibility Segregation,CQRS)
一旦我們實(shí)現(xiàn)了每個(gè)服務(wù)對應(yīng)一個(gè)數(shù)據(jù)庫,就需要將從多個(gè)微服務(wù)查詢返回的數(shù)據(jù)連接起來。顯然這是不可能的。CQRS建議將應(yīng)用分為兩個(gè)部分 — 命令端 (command side)和查詢端 (query side):
- 命令端處理創(chuàng)建,更新和刪除請求
- 查詢端通過使用物化視圖來處理查詢部分
通常 事件溯源模式(event sourcing pattern)和它一起用來為任何數(shù)據(jù)更改創(chuàng)建事件。通過訂閱事件流,可以使物化視圖保持不斷的更新
事件溯源模式(event sourcing pattern)
大多數(shù)應(yīng)用程序都使用數(shù)據(jù),一個(gè)典型的途徑就是應(yīng)用保持當(dāng)前的狀態(tài)。例如,傳統(tǒng)的創(chuàng)建,讀取,更新和刪除(CRUD)中,典型的數(shù)據(jù)處理是從存儲中讀取數(shù)據(jù),它包含經(jīng)常使用事務(wù)鎖定數(shù)據(jù)的限制。
事件溯源模式定義了一系列事件驅(qū)動的數(shù)據(jù)的處理操作,每一個(gè)事件處理操作都會記錄在僅追加存儲中(append-only store)。應(yīng)用程序代碼發(fā)送一系列 命令式的描述了數(shù)據(jù)上發(fā)生的動作的事件到事件持久化存儲的地方。每個(gè)事件代表一組數(shù)據(jù)更改(例如,AddedItemToOrder)
這些事件持久化存儲在充當(dāng)系統(tǒng)記錄系統(tǒng)的事件存儲中。
觀察者模式(Observability Patterns)
日志聚合
考慮這樣一種情況:一個(gè)應(yīng)用包含多個(gè)微服務(wù)實(shí)例,每個(gè)請求經(jīng)常在橫跨多個(gè)微服務(wù)實(shí)例,那么每一個(gè)微服務(wù)實(shí)例都產(chǎn)生一個(gè)表轉(zhuǎn)化格式的日志文件。 因此我們需要一個(gè)中心化的日志服務(wù)來將每個(gè)服務(wù)實(shí)例的日志收集起來。用戶可以搜索分析并分析日志,并且配置一些當(dāng)日志中出現(xiàn)特定信息的報(bào)警規(guī)則。例如:PCF確實(shí)有一個(gè)日志聚合器(Log aggregator), 用來收集PCF平臺上各個(gè)應(yīng)用的各個(gè)組件(router, controller, Diego, 等等…)的日志。AWS Cloud Watch也這樣做。
性能指標(biāo)
因?yàn)槲⒎?wù)架構(gòu)導(dǎo)致服務(wù)的數(shù)量增加時(shí),密切注意事務(wù)變得十分關(guān)鍵,以便監(jiān)控微服務(wù)模式并且在問題發(fā)生的時(shí)候發(fā)出警告。
一個(gè)指標(biāo)服務(wù)用來收集每個(gè)單獨(dú)操作的統(tǒng)計(jì)信息。它應(yīng)該聚合一個(gè)應(yīng)用服務(wù)的所有指標(biāo),以便提供報(bào)告和警報(bào)。 聚合指標(biāo)應(yīng)該包含兩個(gè)模塊:
- 推送 — 服務(wù)推送指標(biāo)給指標(biāo)服務(wù) 例如:NewRelic, AppDynamics
- 拉取 — 指標(biāo)服務(wù)可以從每個(gè)服務(wù)中拉取指標(biāo) 例如:Prometheus
分布式跟蹤
在微服務(wù)架構(gòu)中,請求通常跨越多個(gè)微服務(wù)。 每個(gè)服務(wù)通過跨多個(gè)服務(wù)執(zhí)行一個(gè)或多個(gè)操作來處理一個(gè)請求。 在進(jìn)行故障排除時(shí),有一個(gè)跟蹤ID是非常值得的,這樣我們可以端對端的跟蹤請求。
解決方案是引入一個(gè)事務(wù)ID,可以使用以下方法:
- 為每個(gè)外部請求分配唯一的外部請求ID
- 將外部請求ID傳遞給所有服務(wù)
- 在所有日志消息中包括外部請求ID
健康檢查
實(shí)施微服務(wù)架構(gòu)后,有一種可能是:服務(wù)可能會啟動但無法處理事務(wù)。每個(gè)服務(wù)都需要具有一個(gè)端點(diǎn)用來檢查應(yīng)用的健康程度,例如health。這個(gè)API應(yīng)該檢查主機(jī)的狀態(tài), 與其他服務(wù)/基礎(chǔ)結(jié)構(gòu)的連接以及其他任意特定的邏輯。
交叉關(guān)注模式(Cross-Cutting Concern Patterns)
外部配置(External Configuration)
一個(gè)典型的服務(wù)通常還會調(diào)用其他服務(wù)和數(shù)據(jù)庫,對于每一個(gè)環(huán)境,例如dev, QA, UAT, prod,這些環(huán)境的端點(diǎn)URL或某些配置屬性可能不同,這些屬性中的任何一項(xiàng)更改都可能需要重新構(gòu)建或重新部署服務(wù)。
為了避免代碼修改,我們可以使用配置,將所有的配置信息都外部化,包括端點(diǎn)URL和認(rèn)證信息。應(yīng)用程序應(yīng)該在啟動時(shí)或運(yùn)行時(shí)加載這些配置。這些配置可以在應(yīng)用啟動的時(shí)候訪問到,或者這些配置在不需要重啟服務(wù)的情況下可以更新。
服務(wù)發(fā)現(xiàn)模式
當(dāng)遇見如圖所示的微服務(wù)架構(gòu)時(shí),在微服務(wù)調(diào)用方面我們需要關(guān)注一些問題。
使用容器技術(shù),IP地址可以動態(tài)分配給每個(gè)微服務(wù)實(shí)例, 每次地址更改時(shí),消費(fèi)者服務(wù)的調(diào)用都會中斷,需要手動更改才能恢復(fù)。
消費(fèi)者必須記住每個(gè)服務(wù)URL,并使其緊密耦合。
因此需要?jiǎng)?chuàng)建服務(wù)注冊,該服務(wù)注冊將保存每個(gè)生產(chǎn)型服務(wù)的元數(shù)據(jù)和每個(gè)服務(wù)的說明規(guī)范。服務(wù)實(shí)例在啟動時(shí)應(yīng)注冊到注冊中心,而在實(shí)例關(guān)閉時(shí)應(yīng)注銷。服務(wù)發(fā)現(xiàn)有兩種類型:
- 客戶端,例如:Netflix Eureka
- 服務(wù)端:例如: AWS ALB

斷路器模式
一個(gè)服務(wù)通常會調(diào)用其他服務(wù)來查詢數(shù)據(jù),有一種可能性是下游的服務(wù)會關(guān)閉,這將會帶來兩個(gè)問題:第一個(gè)是:上游服務(wù)繼續(xù)請求關(guān)閉的網(wǎng)絡(luò)服務(wù),直到耗盡網(wǎng)絡(luò)資源,并且降低系統(tǒng)性能。第二個(gè)是:用戶體驗(yàn)將是糟糕的且不可預(yù)測的。
消費(fèi)者應(yīng)通過代理來調(diào)用遠(yuǎn)程服務(wù),該代理的行為類似于電路中的斷路器。當(dāng)連續(xù)的請求失敗的次數(shù)超過閾值時(shí),斷路器將跳閘一段時(shí)間,并且在跳閘的這段時(shí)間內(nèi),所有的調(diào)用遠(yuǎn)程服務(wù)的嘗試都將立即失敗。當(dāng)超過了斷路器跳閘時(shí)間之后,斷路器將允許有限數(shù)量的測試請求通過。如果這些請求成功,則斷路器將恢復(fù)正常操作。否則,如果有一個(gè)請求失敗,則斷路器再次跳閘。對于 一個(gè)應(yīng)用試圖嘗試調(diào)用一個(gè)遠(yuǎn)程服務(wù)或者獲取共享資源,并且該操作很容易的失敗的情況來說, 這個(gè)模式非常適用。

藍(lán)綠發(fā)布模式
在微服務(wù)架構(gòu)中,一個(gè)應(yīng)用程序可以具有許多微服務(wù)。 如果我們在停止所有服務(wù)之后然后部署增強(qiáng)版本,則停機(jī)時(shí)間將是巨大的,并且可能影響業(yè)務(wù)。同樣,回滾將是一場噩夢。藍(lán)綠發(fā)布模式可以避免這種情況。
實(shí)施藍(lán)綠發(fā)布模式可以減少或消除停機(jī)時(shí)間,它通過運(yùn)行兩個(gè)相同的生產(chǎn)環(huán)境:Blue和Green,來實(shí)現(xiàn)這一目標(biāo)。這里我們假設(shè)綠色是已存在的工作實(shí)例,藍(lán)色是該應(yīng)用程序的新版本。在任何時(shí)候,只有一個(gè)環(huán)境處于活動狀態(tài),該活動環(huán)境為所有生產(chǎn)流量提供服務(wù)。 所有云平臺均提供用于實(shí)施藍(lán)綠色部署的選項(xiàng)。
