第三章 微服務架構中的行程間通信
- 1. 微服務架構中的行程間通信概述
- 1.1 互動方式
- 一對一的互動方式
- 一對多的互動方式
- 1.2 在微服務架構中定義API
- 1.3 API的演化
- 1.4 訊息的格式
- 2. 基于同步遠程程序呼叫模式的通信
- 2.1 使用REST
- 2.2 使用gRPC
- 2.3 使用斷路器模式處理區域故障
- 2.4 使用服務發現
- 應用層服務發現模式
- 平臺層服務發現模式
- 3. 基于異步訊息模式的通信
- 3.1 什么是訊息傳遞
- 3.2 使用訊息機制實作互動方式
- 3.3 為基于訊息機制的服務API創建API規范
- 記錄異步操作
- 記錄事件發布
- 3.4 使用訊息代理
- 3.5 處理并發和訊息順序
- 3.6 處理重復訊息
- 撰寫冪等訊息處理程式
- 跟蹤訊息并丟棄重復項
- 3.7 事務性訊息
- 4. 使用異步訊息提高可用性
- 4.1 同步訊息會降低可用性
- 4.2 消除同步互動
當前有多種行程間通信機制供開發者選擇,比較流行的是REST(使用JSON),但是,需要牢記“沒有銀彈”這個大原則,你必須仔細考慮這些選擇,本章將探討各種行程間通信機制,包括REST和訊息傳遞,并討論如何進行權衡,
選擇合適的行程間通信機制是一個重要的架構決策,它會影回應用程式可用性,更重要的是,一個理想的微服務架構應該是在內部由松散耦合的若干服務組成,這些服務使用異步訊息相互通信,REST等同步協議主要用于服務與外部其他應用程式的通信,

1. 微服務架構中的行程間通信概述

1.1 互動方式
有多種客戶端與服務的互動方式,可以分為兩個維度,
第一個維度關注的是一對一和一對多,
- 一對一:每個客戶端請求由一個服務實體來處理,
- 一對多:每個客戶端請求由多個服務實體來處理,
互動方式的第二個維度關注的是同步和異步,
- 同步模式:客戶端請求需要服務端實時回應,客戶端等待回應時可能導致堵塞,
- 異步模式:客戶端請求不會阻塞行程,服務端的回應可以是非實時的,
一對一的互動方式
- 請求/回應:一個客戶端向服務端發起請求,等待回應;客戶端期望服務端很快就會發送回應,在一個基于執行緒的應用中,等待程序可能造成執行緒阻塞,這樣的方式會導致服務的緊耦合,
- 異步請求/回應:客戶端發送請求到服務端,服務端異步回應請求,客戶端在等待回應時不會阻塞執行緒,因為服務端的回應不會馬上就回傳,
- 單向通知:客戶端的請求發送到服務端,但是并不期望服務端做出任何回應,
一對多的互動方式
- 發布/訂閱方式:客戶端發布通知訊息,被零個或者多個感興趣的服務訂閱,
- 發布/異步回應方式:客戶端發布請求訊息,然后等待從感興趣的服務發回的回應,
1.2 在微服務架構中定義API
API或介面是軟體開發的中心,應用是由模塊構成的,每個模塊都有介面,這些介面定義了模塊的客戶端可以呼叫若干操作,一個設計良好的介面會在暴露有用功能同時隱藏實作的細節,因此,這些實作的細節可以被修改,而介面保持不變,這樣就不會對客戶端產生影響,
相比單體架構,我們面臨的挑戰在于:并沒有一個簡單的編程語言結構可以用來構造和定義服務的API,根據定義,服務和它的客戶端并不會一起編譯,如果使用不兼容的API部署新版本的服務,雖然在編譯階段不會出現錯誤,但是會出現運行時故障,
API優先設計
即使在那些最簡單的專案中,組件和API之間也常常發生沖突,例如,負責后端的Java程式員和負責前端的AngularJS程式員都聲稱他們完成了開發,然而這個應用程式卻無法作業,前端的REST和 Web Socket API無法與后端的API一起作業,最終導致這 個應用程式的前端和后端無法正常通信!
服務的API很少一成不變,它可能會隨著時間的推移而發展,讓我們來看看如何做到這點,并討論你將面臨的問題,
1.3 API的演化
語意化版本控制規范為API版本控制提供了有用的指導,它是一組規則,用于指定如何使用版本號,并且以正確的方式遞增版本號,語意化版本控制最初的目的是用軟體包的版本控制,但你可以將其用在分布式系統中對API進行版本控制,
語意化版本控制規范要求版本號由三部分組成: MAJOR. MINOR. PATCH,必須按如下方式遞增版本號:
- MAJOR:當你對API進行不兼容的更改時,
- MINOR:當你對API進行向后兼容的增強時,
- PATCH:當你進行向后兼容的錯誤修復時,
1.4 訊息的格式
- 基于文本的訊息格式(JSON、XML):主要應用HTTP協議
- 二進制訊息格式(Protocol Buffers、Avro):應用gRPC
2. 基于同步遠程程序呼叫模式的通信

當使用基于遠程程序呼叫的行程間通信機制時,客戶端向服務發送請求,服務處理該請求并發回回應,有些客戶端可能會處在堵塞狀態并等待回應,而其他客戶端可能會有一個回應式的非阻塞架構,但與使用訊息機制時不同,客戶端假定回應將及時到達,

2.1 使用REST
好處:
- 它非常簡單,并且大家都很熟悉,
- 可以使用瀏覽器擴展(比如 Postman插件)或者curl之類的命令列(假設使用的是JSON或其他文本格式)來測驗Http api,
- 直接支持請求/回應方式的通信,
- HTTP對防火墻友好,
- 不需要中間代理,簡化了系統架構,
弊端:
- 它只支持請求/回應方式的通信,
- 可能導致可用性降低,由于客戶端和服務直接通信而沒有代理來緩沖訊息,因此它們必須在 REST API呼叫期間都保持在線,
- 客戶端必須知道服務實體的位置(URL),這是現代應用程式中的一個重要問題,客戶端必須使用所謂的服務發現機制來定位服務實體在單個請求中獲取多個資源具有挑戰性,
- 有時很難將多個更新操作映射到HTTP動詞,
雖然存在這些缺點,但REST似乎是API的事實標準,盡管有幾個有趣的替代方案,例如,通過 GraphQL實作靈活、高效的資料提取,
2.2 使用gRPC
好處:
- 設計具有復雜更新操作的API非常簡單,
- 它具有高效、緊湊的行程間通信機制,尤其是在交換大量訊息時,
- 支持在遠程程序呼叫和訊息傳遞程序中使用雙向流式訊息方式,
- 它實作了客戶端和用各種語言撰寫的服務端之間的互操作性,
弊端: - 與基于 RESTASON的API機制相比,JavaScript客戶端使用基于gRPC的API需要做更多的作業,
- 舊式防火墻可能不支持HTTP/2,
gRPC是REST的一個引人注目的替代品,但與REST一樣,它是一種同步通信機制,因此它也存在區域故障的問題,
2.3 使用斷路器模式處理區域故障
分布式系統中,當服務試圖向另一個服務發送同步請求時,永遠都面臨著區域故障的風險,因為客戶端和服務端是獨立的行程,服務端很有可能無法在有限的時間內對客戶端的請求做出回應,服務端可能因為故障或維護的原因而暫停,或者,服務端也可能因為過載而對請求的回應變得極其緩慢,
客戶端等待回應被阻塞,這可能帶來的麻煩就是在其他客戶端甚至使用服務的第三方應用之間傳導,并導致服務中斷,

要通過合理地設計服務來防止在整個應用程式中故障的傳導和擴散,這是至關重要的,
解決這個問題分為兩部分:
- 必須讓遠程程序呼叫代理有正確處理無回應服務的能力,(Netflix Hystrix)
- 需要決定如何從失敗的遠程服務中恢復,(API Gateway回傳備用值)

2.4 使用服務發現
假設你正在撰寫一些呼叫具有REST API的服務的代碼,為了發出請求,你的代碼需要知道服務實體的網路位置(IP地址和埠),在物理硬體上運行的傳統應用程式中,服務實體的網路位置通常是靜態的,例如,你的代碼可以從偶爾更新的組態檔中讀取網路位置,但在現代的基于云的微服務應用程式中,通常不那么簡單,現代應用程式更具動態性,

服務實體具有動態分配的網路位置,此外,由于自動擴展、故障和升級,服務實體集會動態更改,因此,你的客戶端代碼必須使用服務發現,
應用層服務發現模式

這種服務發現方法是兩種模式的組合:
- 自注冊模式:服務實體呼叫服務注冊表的注冊API來注冊其網路位置,它還可以提供運行狀況檢查URL,運行狀況檢查URL是一個API端點,服務注冊表會定期呼叫該端點來驗證服務實體是否正常且可用于處理請求,服務注冊表還可能要求服務實體定期呼叫“心跳”API以防止其注冊過期,
- 客戶端發現模式:當客戶端想要呼叫服務時,它會査詢服務注冊表以獲取服務實體的串列,為了提高性能,客戶端可能會快取服務實體,然后,服客戶端使用負載平衡演算法(例如回圈或隨機)來選擇服務實體,然后它向選擇的服務實體發出請求,
應用層服務發現的一個好處是它可以處理多平臺部署的問題(服務發現機制與具體的部署平臺無關)其余服務在遺留環境中運行,在這種情況下,使用 Eureka的應用層服務發現同時適用于兩種環境,而基于Kubernetes的服務發現僅能用于部署在 Kubernetes平臺之上的部分服務,
應用層服務發現的一個弊端是:你需要為你使用的每種編程語言(可能還有框架)提供服務發現庫,Node. js或GoLang則必須找到其他一些服務發現框架,應用層服務發現的另一個弊端是開發者負責設定和管理服務注冊表,這會分散一定的精力,因此,最好使用部署基礎設施提供的服務發現機制,
平臺層服務發現模式
許多現代部署平臺(如Docker和Kubernetes)都具有內置的服務注冊表和服務發現機制,部署平臺為每個服務提供DNS名稱、虛擬IP(VIP)地址和決議為VIP地址的DNS名稱,客戶端向DNS名稱和VIP發出請求,部署平臺自動將請求路由到其中一個可用服務實體,因此,服務注冊、服務發現和請求路由完全由部署平臺處理,

這種方法是以下兩種模式的組合:
- 第三方注冊模式:由第三方負責(稱為注冊服務器,通常是部署平臺的一部分)處理注冊,而不是服務本身向服務注冊表注冊自己,
- 服務端發現模式:客戶端不再需要查詢服務注冊表,而是向DNS名稱發出請求對該DNS名稱的請求被決議到路由器,路由器查詢服務注冊表并對請求進行負載均衡,
由平臺提供服務發現機制的主要好處是服務發現的所有方面都完全由部署平臺處理,服務和客戶端都不包含任何服務發現代碼,因此,無論使用哪種語言或框架,服務發現機制都可供所有服務和客戶使用,
平臺提供服務發現機制的一個弊端是它僅限于支持使用該平臺部署的服務,例如,如前所述,在描述應用程式級別發現時,基于 Kubernetes的發現僅適用于在 Kubernetes上運行的服務,盡管存在此限制,我建議盡可能使用平臺提供的服務發現,
3. 基于異步訊息模式的通信

使用訊息機制時,服務之間的通信采用異步交換訊息的方式完成,基于訊息機制的應用程式通常使用訊息代理,它充當服務之間的中介,另一種選擇是使用無代理架構,通過直接向服務發送訊息來執行服務請求,服務客戶端通過向服務發送訊息來發出請求,如果希望服務實體回復,服務將通過向客戶端發送單獨的訊息的方式來實作,由于通信是異步的,因此客戶端不會堵塞和等待回復,相反,客戶端都假定回復不會馬上就收到,
3.1 什么是訊息傳遞

訊息的不同型別:
- 檔案:僅包含資料的通用訊息,接收者決定如何解釋它,對命令式訊息的回復是檔案訊息的一種使用場景,
- 命令:一條等同于RPC請求的訊息,它指定要呼叫的操作及其引數,
- 事件:表示發送方這一端發生了重要的事件,事件通常是領域事件,表示領域物件的狀態更改,
兩種型別的訊息通道:
- 點對點通道:例如,命令式訊息通常通過點對點通道發送,
- 發布-訂閱通道:例如,事件式訊息通常通過發布一訂閱通道發送,
3.2 使用訊息機制實作互動方式
- 實作請求/回應和異步請求/回應

- 實作單向通知
- 實作發布/訂閱
- 實作發布/異步回應
3.3 為基于訊息機制的服務API創建API規范

如圖所示,服務的異步API規范必須指定訊息通道的名稱、通過每個通道交換的訊息型別及其格式,你還必須使用諸如JSON、XML或 Protobuf之類的標準來描述訊息的格式,但與REST和 Open API不同,并沒有廣泛采用的標準來記錄通道和訊息型別,你需要自己撰寫這樣的檔案,
記錄異步操作
- 請求/異步回應式API:包括服務的命令訊息通道、服務接受的命令式訊息的具體型別和格式,以及服務發送的回復訊息的型別和格式,
- 單向通知式API:包括服務的命令訊息通道,以及服務接受的命令式訊息的具體型別和格式,
記錄事件發布
服務還可以使用發布/訂閱的方式對外發布事件,此API風格的規范包括事件通道以及服務發布到通道的事件式訊息的型別和格式,
3.4 使用訊息代理

基于訊息傳遞的應用程式通常使用訊息代理,即服務通信的基礎設施服務,但基于訊息代理的架構并不是唯一的訊息架構,你還可以使用基于無代理的訊息傳遞架構,其中服務直接相互通信,這兩種方法具有不同的利弊,但通常基于代理的架構是一種更好的方法,
無代理的架構好處:
- 允許更輕的網路流量和更低的延遲,因為訊息直接從發送方發送到接收方,而不必從發送方到訊息代理,再從代理轉發到接收方,
- 消除了訊息代理可能成為性能瓶頸或單點故障的可能性,
- 具有較低的操作復雜性,因為不需要設定和維護訊息代理,
弊端
- 服務需要了解彼此的位置,因此必須使用服務發現機制,
- 會導致可用性降低,因為在交換訊息時,訊息的發送方和接收方都必須同時在線,
- 在實作例如確保訊息能夠成功投遞這些復雜功能時的挑戰性更大,
實際上,這些弊端中的一些(例如可用性降低和需要使用服務發現),與使用同步請求/回應互動方式所導致的弊端相同,
有許多訊息代理可供選擇,流行的開源訊息代理包括:
- Apache ActiveMQ
- RabbitMQ
- Apache Kafka
基于代理的訊息的好處和弊端
好處:
- 松耦合:客戶端發起請求時只要發送給特定的通道即可,客戶端完全不需要感知服務實體的情況,客戶端不需要使用服務發現機制去獲得服務實體的網路位置,
- 訊息快取:訊息代理可以在訊息被處理之前一直快取訊息,像HTTP這樣的同步請求/回應協議,在交換資料時,發送方和接收方必須同時在線,然而,在使用訊息機制的情況下,訊息會在佇列中快取,直到它們被接收方處理,這就意味著,例如,即使訂單處理系統暫時離線或不可用,在線商店仍舊能夠接受客戶的訂單,訂單訊息將會在佇列中快取(并不會丟失),
- 靈活的通信:訊息機制支持前面提到的所有互動方式,
- 明確的行程間通信:基于RPC的機制總是企圖讓遠程服務呼叫跟本地呼叫看上去沒什么區別(在客戶端和服務端同時使用遠程呼叫代理),然而,因為物理定律(如服務器不可預計的硬體失效)和可能的區域故障,遠程和本地呼叫還是大相徑庭的,訊息機制讓這些差異變得很明確,這樣程式員不會陷入一種“太平盛世”的錯覺,
弊端:
- 潛在的性能瓶頸:訊息代理可能存在性能瓶頸,幸運的是,許多現代訊息代理都支持高度的橫向擴展,
- 潛在的單點故障:訊息代理的高可用性至關重要,否則系統整體的可靠性將受到影響,幸運的是,大多數現代訊息代理都是高可用的,
- 額外的操作復雜性:訊息系統是一個必須獨立安裝、配置和運維的系統組件,
3.5 處理并發和訊息順序
挑戰之一是如何在保留訊息順序的同時,橫向擴展多個接收方的實體,為了同時處理訊息,擁有多個實體是一個常見的要求,而且,即使單個服務實體也可能使用執行緒來同時處理多個訊息,使用多個執行緒和服務實體來并發處理訊息可以提高應用程式的吞吐量,但同時處理訊息的挑戰是確保每個訊息只被處理一次,并且是按照它們發送的順序來處理的,

現代訊息代理(Apache Kafka)使用的常見解決方案是使用分片(磁區)通道,
- 并發:分片處理并發,
- 順序:特定訂單的每個事件都放到同一個分片,
3.6 處理重復訊息
使用訊息機制時必須解決的另一個挑戰是處理重復訊息,理想情況下,訊息代理應該只傳遞一次訊息,但保證有且僅有一次的訊息傳遞通常成本很高,相反,大多數訊息代理承諾至少成功傳遞一次訊息,
處理重復訊息有以下兩種不同的方法,
撰寫冪等訊息處理程式
如果應用程式處理訊息的邏輯是滿足冪等的,那么重復的訊息就是無害的,不幸的是,應用程式邏輯通常不是冪等的,或者你可能正在使用訊息代理,該訊息代理在重新傳遞訊息時不會保留排序,重復或無序訊息可能會導致錯誤,
跟蹤訊息并丟棄重復項
例如,考慮一個授權消費者信用卡的訊息處理程式,它必須為每個訂單僅執行一次信用卡授權操作,這段應用程式邏輯在每次呼叫時都會產生不同的效果,如果重復訊息導致訊息處理程式多次執行該邏輯,則應用程式的行為將不正確,執行此類應用程式邏輯的訊息處理程式必須通過檢測和丟棄重復訊息而成為冪等的,
一個簡單的解決方案是訊息接收方使用 message id跟蹤它已處理的訊息并丟棄任何重復項,例如,它可以存盤它在資料庫表中使用的每條訊息的 message id,

當接收方處理訊息時,它將訊息的 message id作為創建和更新業務物體的事務的部分記錄在資料庫表中,在此示例中,接收方將包含 message id的行插入 PROCESSED MESSAGES表,如果訊息是重復的,則 INSER將失敗,接收方可以選擇丟棄該訊息,
另一個選項是訊息處理程式在應用程式表,而不是專用表中記錄 message id,當使用具有受限事務模型的 NoSQL資料庫時,此方法特別有用,因為 NOSQL資料庫通常不支持將針對兩個表的更新作為資料庫的事務,
3.7 事務性訊息
服務通常需要在更新資料庫的事務中發布訊息,如果服務不以原子方式執行這兩個操作則類似的故障可能使系統處于不一致狀態,
傳統的解決辦法是在資料庫和訊息代理之間使用分布式事務,然而,分布式事務對現今的應用程式而言并不是一個很好的選擇,而且,很多新的訊息代理不支持分布式事務,
解決方案:
- 使用資料庫表作為訊息佇列

我們假設你的應用程式正在使用關系型資料庫,可靠地發布訊息的直接方法是應用事務性發件箱模式,此模式使用資料庫表作為臨時訊息佇列,如圖所示,發送訊息的服務有一個OUTBOX資料庫表,作為創建、更新和洗掉業務物件的資料庫事務的一部分,服務通過將訊息插入到OUTBOX表中來發送訊息,這樣可以保證原子性,因為這是本地的ACID事務,通過輪詢資料庫中的發件箱來發布訊息, - 訊息相關的類別庫和框架(Eventuate Tram)
實作了兩個重要機制:
1.事務性訊息機制:它將訊息作為資料庫事務的一部分發布,
2.重復訊息檢測機制:支持訊息的接收方檢測并丟棄重復訊息,這對于確保接收方只準確處理訊息一次至關重要,
4. 使用異步訊息提高可用性

4.1 同步訊息會降低可用性
REST是一種非常流行的行程間通信機制,你可能很想將它用于服務間通信,但是,REST的問題在于它是一個同步協議:HTTP客戶端必須等待服務端回傳回應,只要服務使用同步協議進行通信,就可能降低應用程式的可用性,
從統計意義上講,一個系統操作的可用性,由其所涉及的所有服務共同決定,如果Order service服務和它所呼叫的兩個服務的可用性都是99.5%3,那么這個系統操作的整體可用性就是99.5%3,大約是98.5%3,這其實是個非常低的數值了,每一個額外增加的服務參與到其中,都會更進一步降低整體系統操作的可用性,
4.2 消除同步互動
- 使用異步互動模式

- 復制資料

- 先回傳回應,再完成處理

1.僅使用本地的資料來完成請求的驗證,
2.更新資料庫,包括向 OUTBOⅩ表插入訊息,
3.向客戶端回傳回應,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/259400.html
標籤:其他
下一篇:紅包系統流量高并發技術詳解
