概述
微服務能在企業(yè)中發(fā)揮積極作用抗悍。因此了解微服務架構(gòu)(MSA)設(shè)計的一般目標或原則燕雁,以及一些微服務的設(shè)計模式瘸味,都是是很有意義的
- 降低成本:MSA 降低了 IT 服務的設(shè)計、實現(xiàn)和管理的總體成本
- 提高交付速度:MSA 能夠提高服務的實現(xiàn)速度
- 增強健壯性:MSA 能夠增強我們服務網(wǎng)絡(luò)的健壯性
- 提供可視化支持:MSA 能夠為服務和網(wǎng)絡(luò)提供更好的可視化支持
微服務架構(gòu)的構(gòu)建原則
- 伸縮能力
- 可用性
- 健壯性
- 彈性
- 獨立的匿名服務
- 去中心化的治理
- 故障隔離
- 自動供給
- 通過 DevOps 實現(xiàn)持續(xù)交付
在系統(tǒng)建設(shè)中彻坛,堅持上述原則會遭遇很多挑戰(zhàn)和問題。這些問題在很多解決方案中都會出現(xiàn)踏枣。如果能夠正確的使用合適的設(shè)計模式昌屉,就能夠克服這些問題。微服務的設(shè)計模式可以分為五大類茵瀑,每個大類中都包含一些設(shè)計模式
拆分模式
根據(jù)業(yè)務拆分
根據(jù)業(yè)務能力進行分解的方式间驮,能降低服務耦合度,實現(xiàn)單一職責的服務目標马昨。這里說的業(yè)務能力是來自業(yè)務架構(gòu)模型的一個概念竞帽,是企業(yè)用來創(chuàng)造價值的行為。業(yè)務能力通常對應到業(yè)務對象上偏陪,例如:
- 訂單管理負責訂單
- 客戶管理負責客戶
利用子域拆分
使用業(yè)務能力對應用進行拆分是個好的開端抢呆,但是往往會遇到不易分解的超級類。這種超級類在很多服務中都很普遍笛谦。用 DDD 子域來進行服務的定義抱虐。DDD 把應用的問題空間(業(yè)務)當作領(lǐng)域忍宋。一個領(lǐng)域由多個子域構(gòu)成逞刷。每個子域負責業(yè)務的不同部分。子域可以分為幾類:
- 核心: 業(yè)務中的關(guān)鍵差異因素颂砸,也是應用程序中的價值核心
- 支撐: 和業(yè)務有關(guān)灶轰,但并非關(guān)鍵差異谣沸;可以自建,也可以外包
- 通用: 并不針對業(yè)務笋颤,理想情況下乳附,可以使用現(xiàn)成的軟件實現(xiàn)
訂單管理的子域包括:
- 產(chǎn)品類目服務
- 庫存管理服務
- 訂單管理服務
- 配送管理服務
根據(jù)事務拆分
這種模式可以分解事務上的服務,這樣系統(tǒng)中就會有會有多個事務伴澄。分布式事務中的一個重要概念就是事務協(xié)調(diào)者赋除。分布式事務 (兩階段提交) 由兩個步驟組成:
- 準備階段: 這個階段中,事務的所有參與者都做好準備非凌,并通知事務協(xié)調(diào)者举农,它們已經(jīng)準備好完成事務
- 提交或歸滾階段: 這個階段里,事務協(xié)調(diào)者會向所有參與方發(fā)出提交或回滾命令
注意: 相對于單一微服務來說敞嗡,兩段提交的問題就是慢颁糟。在微服務之間進行事務協(xié)調(diào)航背,就算是在同一網(wǎng)絡(luò)中,也會拖慢系統(tǒng)棱貌;所以這種方式在高負載場景中并不常見
扼殺者模式
前面三個設(shè)計模式都是對 綠地 (Greenfiled) 應用進行拆分玖媚,但是還有 80% 的 棕地 (Brownfiled) 需要對付 —— 它們是傳統(tǒng)、龐大的單體應用键畴。扼殺者模式為此而生最盅。這種模式會創(chuàng)建兩個獨立的應用,一同運行在同樣的 URI 空間中起惕。隨著時間點的推移涡贱,新的重構(gòu)了的應用會扼殺或者替換掉原有應用,最后就可以關(guān)掉單體應用了惹想。這種模式分為 轉(zhuǎn)換问词、共存 和 終結(jié) 三個步驟
艙壁模式
這個模式把應用的元素隔離開來,這樣一個失敗之后嘀粱,其它的還能繼續(xù)工作激挪。這個模式可以類比船體結(jié)構(gòu),因此被稱為艙壁锋叨。根據(jù)消費者的負載以及可用性要求垄分,把服務分割為不同的群。這種設(shè)計能夠?qū)收线M行隔離娃磺,即使遇到故障薄湿,也能為部分消費者提供服務
注: 艙壁模式(Bulkhead)隔離了每個工作負載或服務的關(guān)鍵資源,如連接池偷卧、內(nèi)存和 CPU豺瘤,使用艙壁避免了單個工作負載(或服務)消耗掉所有資源,從而導致其他服務出現(xiàn)故障的場景听诸,這種模式主要是通過防止由一個服務引起的級聯(lián)故障來增加系統(tǒng)的彈性(艙壁模式降低依賴服務對整個系統(tǒng)的影響坐求,保護有限的資源不被耗盡,增加了系統(tǒng)得到彈性)
Sidecar 模式
這種模式把應用的組件部署到一個不同的容器中晌梨,從而更好地完成隔離和封裝桥嗤。這種模式讓應用能夠把多種組件和技術(shù)整合在一起。這種模式的情況很像摩托車的挎斗仔蝌,因此被稱為 Sidecar砸逊,Sidecar 附著在主應用上,并且為主應用提供支持能力掌逛。Sidecar 還和主應用共享同樣的生命周期,它的創(chuàng)建和銷毀都是和主應用同步進行的司倚。Sidecar 模式有時也被稱為 Sidekick 模式
集成模式
API 網(wǎng)關(guān)模式
應用被分解成更小的微服務之后豆混,就會出現(xiàn)一些待解決的問題
- 如何處理來自不同渠道的不同微服務的調(diào)用
- 如何處理不同的協(xié)議
- 不同消費者可能需要不同格式的響應
API 網(wǎng)關(guān)就是用來處理這類問題的
- API 網(wǎng)關(guān)是所有微服務調(diào)用的單一入口
- 可以作為代理服務器篓像,將特定請求路由到特定微服務
- 可以把調(diào)用結(jié)果進行聚合,發(fā)回給消費者
- 可以為每種類型的客戶端創(chuàng)建細粒度的 API
- 還能轉(zhuǎn)化請求和響應的協(xié)議
- 可以代微服務進行認證皿伺、鑒權(quán)的工作
聚合模式
業(yè)務功能被分拆為多個更小的代碼段之后员辩,如何把各個微服務返回的數(shù)據(jù)進行整合就是個問題了。這種責任不應該拋給消費者自行解決鸵鸥。聚合模式可以解決這種問題奠滑,這種模式的關(guān)鍵是如何把多個不同服務的響應數(shù)據(jù)進行聚合,然后將最終響應發(fā)回給消費者妒穴∷嗡埃可以用兩種方式來完成任務:
- 用一個復合微服務調(diào)用所有必須的微服務,把數(shù)據(jù)拼裝成合適的結(jié)果發(fā)回給客戶端
- 用 API 網(wǎng)關(guān)把請求拆分為對多個微服務的調(diào)用讼油,然后聚合返回結(jié)果發(fā)回給客戶端
如果這一過程中有業(yè)務邏輯杰赛,推薦使用復合微服務的方式。其它情況下矮台,API 網(wǎng)關(guān)是個好方法
代理模式
這種 API 網(wǎng)關(guān)只會使用 API 網(wǎng)關(guān)開放微服務乏屯。例如一個 API 網(wǎng)關(guān)有三個 API 模塊:
- 移動 API: 為手機客戶端提供 API
- 瀏覽器 API: 為瀏覽器中運行的 JavaScript 應用提供 API
- 公共 API: 為第三方開發(fā)者提供的 API
路由網(wǎng)關(guān)模式
這種 API 網(wǎng)關(guān)負責對請求進行路由。它通過將請求路由給特定服務的方式來完成 API 調(diào)用瘦赫。當它接到請求的時候辰晕,會根據(jù)請求在路由表中查找合適的服務。舉個例子來說确虱,路由表可能會將一個 HTTP 方法和路徑映射為服務的 HTTP URL含友。這種能力和 NGINX 等服務器的反向代理功能一致
鏈式微服務模式
有的微服務會有多種依賴,例如銷售服務依賴于產(chǎn)品和訂單服務蝉娜。鏈式微服務模式能夠為請求提供合并的結(jié)果唱较,微服務 1 收到的請求會向后傳遞給微服務 2 和微服務 3。所有這些服務都是同步調(diào)用
分支模式
微服務可能需要從多個數(shù)據(jù)源(或者微服務)獲取數(shù)據(jù)召川。分支模式是聚合模式和鏈式模式的混合體南缓,用來并行的處理兩個或更多個微服務進行交互。被調(diào)用的微服務可以是一個微服務組成的鏈條荧呐。分支模式還可以根據(jù)業(yè)務需要汉形,調(diào)用多個或者一個微服務鏈條
客戶端分解模式
根據(jù)業(yè)務功能或者子域進行解耦,完成服務開發(fā)之后倍阐,負責用戶體驗的服務必須從多個微服務拉取數(shù)據(jù)概疆。在單體服務的世界中,UI 接收到請求之后峰搪,只需要一次后端調(diào)用就能夠獲取所有數(shù)據(jù)岔冀,從而完成刷新或提交動作。然而現(xiàn)在不同了概耻。在微服務的環(huán)境下使套,UI 要把頁面 / 屏幕分割為多個區(qū)域罐呼。每個區(qū)域會調(diào)用獨立的微服務來獲取數(shù)據(jù)。AngularJS 或者 ReactJS 這樣的框架能夠簡化這些工作侦高。在單頁面應用(SPA)中嫉柴,每個微服務都有自己對應的頁面組件。UI 團隊要負責把多個特定服務的 UI 組件組裝起來奉呛,形成頁面骨架计螺,最終完成整體頁面 / 屏幕的輸出
數(shù)據(jù)庫模式
在微服務中定義數(shù)據(jù)庫架構(gòu),需要考慮幾個要點:
- 服務必須松耦合瞧壮〉锹可以獨立的被開發(fā)、部署以及擴縮容
- 業(yè)務事務可能需跨越多個服務
- 有的業(yè)務事務需要查詢隸屬于多個服務的多種數(shù)據(jù)
- 數(shù)據(jù)庫必須能夠被復制或者共享從而滿足規(guī)模要求
- 不同服務有不同的數(shù)據(jù)存儲需求
服務獨占數(shù)據(jù)庫
為了滿足上述要素馁痴,每個服務必須擁有各自的數(shù)據(jù)庫谊娇;數(shù)據(jù)庫必須被特定服務所獨占。對這些獨占數(shù)據(jù)是不能直接訪問的罗晕,只能通過微服務 API 進行數(shù)據(jù)訪問济欢。例如關(guān)系型數(shù)據(jù)庫來說,可以用服務專屬的數(shù)據(jù)表小渊、專屬結(jié)構(gòu)或者專屬數(shù)據(jù)庫
服務共享數(shù)據(jù)庫
微服務領(lǐng)域的理想情況就是每個服務都獨占數(shù)據(jù)庫法褥。那么共享數(shù)據(jù)庫就是反模式的。但是在單體應用拆分為微服務的過程中可就沒那么容易了酬屉。后面的階段中半等,可以轉(zhuǎn)向每個服務獨占數(shù)據(jù)庫的模式。共享數(shù)據(jù)庫并不理想呐萨,但是在遷移過程中是有用的杀饵。多數(shù)人會認為這不符合微服務需求,但是對于既有應用谬擦,這是一個好的拆分起點切距。綠地應用就不該這樣了
命令查詢隔離
命令和查詢的隔離 (CQRS),一旦實現(xiàn)了服務獨占數(shù)據(jù)的模式惨远,就有了拼接多個服務的數(shù)據(jù)的需要谜悟。CQRS 把應用分成兩部分 —— 命令和查詢
- 命令端用來處理創(chuàng)建、更新和刪除
- 查詢端用物化視圖來處理查詢
事件源模式會為任何數(shù)據(jù)變更創(chuàng)建事件北秽。物化視圖訂閱事件流葡幸,以此來保持更新。事件源模式的典型用法就是根據(jù)數(shù)據(jù)來管理應用程序的當前狀態(tài)贺氓。例如傳統(tǒng)的增刪改查模型是從存儲讀取數(shù)據(jù)的蔚叨。這其中存在鎖定數(shù)據(jù)的需要,通常要用事務來解決
事件源模式
事件源模式定義了處理數(shù)據(jù)操作的方法,以事件序列進行驅(qū)動蔑水,每個事件都記錄在只支持追加寫入的存儲之中悄泥。應用程序會發(fā)送一系列事件,這些事件把每個數(shù)據(jù)操作強制寫入到存儲之中進行持久化肤粱。每個事件代表對數(shù)據(jù)的一些修改(例如 AddedItemToOrder)。這些事件保存在事件庫中厨相。事件庫中發(fā)布的事件领曼,其典型用途就是在應用修改實體時,維持物化視圖的狀態(tài)蛮穿,可以用于外部系統(tǒng)的集成庶骄。例如一個系統(tǒng)可以維護一個所有客戶訂單的物化視圖,這個視圖用于在 UI 中展示践磅。在應用加入新訂單单刁、或者在訂單的項目中進行增刪以及修改送貨信息時,這些變更產(chǎn)生的事件就可以用來更新物化視圖府适。下圖描述了這個模式的概況
Saga 模式
在每個服務都有了自己的數(shù)據(jù)庫之后羔飞,如果出現(xiàn)了跨服務的事務的時候,如何確認服務的數(shù)據(jù)一致性呢檐春?在請求失敗時逻淌,每個請求都要執(zhí)行一個補償請求∨迸可以用兩種方式來實現(xiàn)
- 沒有中央?yún)f(xié)調(diào)機制卡儒,每個服務都會生成事件,也監(jiān)聽別的服務的事件俐巴,從而對是否采取動作進行決策骨望。這是一種兩方或多方的協(xié)作方式,任何一方都沒有控制其它成員的能力欣舵,甚至可能對其它成員都是不可見的擎鸠。這種做法能夠?qū)顒舆M行協(xié)調(diào),并且能分享信息和價值邻遏。在跨越域或者可見區(qū)域進行協(xié)調(diào)時就可以用這種方法糠亩。在簡單場景中,可以和網(wǎng)絡(luò)協(xié)議類比准验,它定義了各參與方之間的請求和響應模式
- 使用編排器負責決策赎线,對業(yè)務邏輯進行排序。如果能夠?qū)^程中所有參與者都有控制權(quán)糊饱,并且他們都存在于同一個控制域垂寥,就可以控制活動的流程了。這種情況多見于組織內(nèi)部的業(yè)務流程
觀察模型
日志聚合
日志聚合針對的是包含多個服務的應用。請求經(jīng)常會跨越多個服務實例滞项。每個服務實例都生成標準格式的日志文件狭归。我們需要一個中央日志服務,把各個服務實例的日志聚合起來文判。用戶可以對日志進行搜索和分析过椎。可以對日志系統(tǒng)進行配置戏仓,如果出現(xiàn)了特定信息疚宇,就觸發(fā)告警。例如 PCF 的日志聚合器會從 PCF 的每個組件(router赏殃、controller敷待、diego 等)和應用中搜集日志。AWS 的 Cloud Watch 也做了同樣的事情
性能指標
微服務架構(gòu)下服務數(shù)量會急劇增加仁热,就需要提高監(jiān)控的重視程度榜揖,在問題發(fā)生時,才能及時的發(fā)送警報抗蠢。需要有一個度量服務來收集各種統(tǒng)計信息举哟。應用提供的報告和告警都應該發(fā)送給這個服務。聚合指標有兩種模型:
- 推: 服務把指標推送給監(jiān)控服務物蝙,例如 NewRelic炎滞、AppDynamics
- 拉: 監(jiān)控服務自行拉取指標數(shù)據(jù),例如 Prometheus
分布式追蹤
微服務架構(gòu)下诬乞,請求經(jīng)常會跨越多個服務册赛。每個服務又要用一個或多個操作,調(diào)用多個服務來處理請求震嫉。要對請求進行端到端的跟蹤森瘪,有個跟蹤 ID 會非常有幫助∑倍拢可以用下列方法來引入事務 ID 從而解決這一問題:
- 為每個外部請求分配一個唯一的外部請求 ID
- 把外部請求的 ID 傳遞到所有服務
- 在所有日志信息中輸出這一外部的請求 ID
健康檢查
實現(xiàn)了微服務架構(gòu)之后扼睬,微服務自身可能出現(xiàn)無法處理業(yè)務的情況。每個服務都需要有一個端點悴势,用來檢查應用的健康情況窗宇。這個 API 可以檢查主機的狀態(tài)、到其它服務特纤、基礎(chǔ)設(shè)施的連接情況军俊,以及一些別的邏輯
跨領(lǐng)域模式
外部化配置
服務通常會調(diào)用其它服務以及數(shù)據(jù)庫。多個環(huán)境中捧存,例如開發(fā)粪躬、測試担败、生產(chǎn)等,端點地址或者一些配置屬性可能不同镰官。這些屬性中的任何變動都可能需要服務的重新構(gòu)建和部署提前。為了這種情況,建議使用外部配置泳唠,例如 URL 和登錄憑據(jù)狈网。應用應該在啟動時或者隨時載入配置
服務發(fā)現(xiàn)模式
實現(xiàn)微服務時,我們需要解決一些調(diào)用服務的問題笨腥。在容器環(huán)境中孙援,IP 地址是動態(tài)分配給服務實例的。每一次地址變更扇雕,消費者服務都會受到影響,需要隨之變更窥摄。消費者服務需要記住每個服務的地址镶奉,這樣就形成了一種緊耦合≌阜牛可以創(chuàng)建服務注冊表哨苛,用于保存每個生產(chǎn)者服務的元數(shù)據(jù)以及規(guī)范信息。服務實例在啟動時應該進行注冊币砂,并在關(guān)閉時解除注冊建峭。服務發(fā)現(xiàn)有兩種方式:
- 客戶端: 例如 Netflix Eureka
- 服務端: 例如 AWS ALB
熔斷模式
服務通常需要調(diào)用其它服務來獲取數(shù)據(jù),有時候下游服務會出現(xiàn)故障無法提供服務决摧。這種情況下有兩個問題:
- 首先亿蒸,請求會持續(xù)發(fā)送給宕機的服務,耗盡網(wǎng)絡(luò)資源掌桩,降低性能
- 其次边锁,用戶體驗會變差,結(jié)果也無法預測
消費者應該通過代理服務器調(diào)用遠程服務波岛,這個代理可以扮演電路中的熔斷器的角色茅坛。當錯誤持續(xù)出現(xiàn)到一個閾值時,熔斷行為被觸發(fā)则拷,在一定時間內(nèi)贡蓖,所有調(diào)用該服務的請求都會立刻失敗。時間窗口過后煌茬,熔斷器允許一定數(shù)量的訪問嘗試斥铺。如果這些嘗試成功了,熔斷器恢復為正常的放行狀態(tài)宣旱;如果再次出現(xiàn)故障仅父,就再次進入開路狀態(tài)叛薯。要調(diào)用一個容易故障的服務或共享資源,這種模式非常有用
藍綠部署模式
在微服務架構(gòu)中笙纤,一個應用會由多個服務構(gòu)成耗溜,如果停掉所有服務,部署一個增強版本省容,停機時間會對業(yè)務造成很大影響抖拴。同樣回滾過程也會是一個噩夢。藍綠部署模式避免了這種問題腥椒。藍綠部署的策略能夠降低或免除服務的停機時間阿宅。這種策略同時運行兩個一致的生產(chǎn)環(huán)境,用這種方式來解決問題笼蛛。假設(shè)綠色是現(xiàn)存的服務實例洒放,而藍色是新版本。任何時間里滨砍,只有一個環(huán)境是在線的往湿,這個在線環(huán)境會處理所有生產(chǎn)通信。所有的云平臺都提供了藍綠部署的支持