服務對服務通訊
移轉自前端用戶端,我們現在可以處理後端微服務彼此之間的通訊。
建構雲端原生應用程式時,您會想要對後端服務彼此通訊的方式反應機敏。 在理想情況下,服務間的通訊愈少愈好。 不過,後端服務通常會互相依賴以完成作業,所以彼此通訊不可能避免。
有數種廣為大眾接受的方法可實作跨服務通訊。 通訊互動的類型通常會決定最佳方法。
請考慮下列互動類型:
查詢 – 當呼叫方微服務需要來自被呼叫方微服務的回應時,例如:「您好,請給我指定客戶識別碼的購買者資訊。」
命令 – 當呼叫方微服務需要其他微服務執行動作,但不需要回應時,例如:「您好,只要寄送此訂單即可。」
事件 – 當微服務 (即發行者) 引發狀態已變更或動作已發生的事件時。 其他有意的微服務 (即訂閱者) 可以適當方式回應該事件。 發行者和訂閱者不會察覺到彼此。
微服務系統在執行需要跨服務互動的作業時,通常會使用這些互動類型的組合。 讓我們仔細看看各項微服務,以及您該如何實作它們。
查詢
很多時候,一項微服務可能必須查詢另一項微服務,需要獲得立即回應才能完成作業。 購物籃微服務需要有產品資訊和價格,才能將項目新增至其購物籃。 實作查詢作業的方法有很多種。
要求/回應傳訊
實作此案例的其中一個選項,是讓呼叫方後端微服務向其需要查詢之微服務提出直接的 HTTP 要求,如圖 4-8 所示。
圖 4-8: 直接 HTTP 通訊
雖然微服務之間的直接 HTTP 呼叫相當容易實作,但仍應謹慎小心以將這項做法減少至最低。 啟動時,這些呼叫一律同步而且會封鎖作業,直到傳回結果或要求逾時為止。 以前在獨立後就能獨立發展並經常部署的獨立服務,現在會彼此結合。 隨著微服務之間的結合增加,其架構優點隨之降低。
有些系統可能會接受執行非經常性的要求,向其他微服務進行單一直接的 HTTP 呼叫。 但不建議向多項微服務叫用直接 HTTP 呼叫的大量呼叫。 它們會增加延遲,並對系統的效能、可擴縮性和可用性造成負面影響。 更糟的是,一連串的直接 HTTP 通訊會導致同步微服務呼叫的深度和複雜鏈結,如圖 4-9 所示:
圖 4-9。 鏈結 HTTP 查詢
由上圖的設計,您可以想像得到其中的風險。 如果步驟 #3 失敗,會發生什麼事? 如果步驟 #8 失敗呢? 該如何復原? 如果步驟 #6 因為基礎服務忙碌而變慢,該怎麼辦? 該如何繼續? 即使所有工作都正常運作,請考慮此呼叫會產生的延遲,這會是每個步驟的延遲總和。
上圖中的高度結合,表示服務並未以最佳方式建立模型。 團隊應重新審視其設計。
具體化檢視模式
移除微服務結合的熱門選項是具體化檢視模式。 使用此模式時,微服務會儲存其他服務所擁有之自己本機的反正規化資料複本。 它會維護自己的本機資料複本,而不是讓購物籃微服務查詢產品目錄和定價等微服務。 此模式可消除不必要的結合,並改善可靠性和回應時間。 整個作業是在單一處理序內執行。 我們在第 5 章中探索此模式和其他資料考量。
服務彙總工具模式
另一個排除微服務對微服務結合的選項是彙總工具微服務,如圖 4-10 的紫色部分。
圖 4-10: 彙總工具微服務
此模式會隔離向許多後端微服務發出呼叫的作業,將其邏輯集中至特殊化微服務。 上圖中的紫色簽出彙總工具微服務會協調簽出作業的工作流程。 包含依循序順序向數項後端微服務發出的呼叫。 工作流程中的資料會彙總並傳回呼叫端。 雖然仍實作直接 HTTP 呼叫,但彙總工具微服務可減少後端微服務間的直接相依性。
要求/回復模式
分離同步 HTTP 訊息的另一個方法是使用佇列通訊的要求/回復模式。 使用佇列通訊一律是單向通道,產生者傳送訊息而取用者接收訊息。 使用此模式時,會實作要求佇列和回應佇列,如圖 4-11 所示。
圖 4-11: 要求/回復模式
在此模式中,訊息產生者建立包含唯一相互關聯識別碼的查詢型訊息,並將它放入要求佇列。 取用服務會清除佇列訊息、處理訊息,然後將回應放入具有相同相互關聯識別碼的回應佇列。 產生者服務會清除佇列訊息,比對相互關聯識別碼,然後繼續處理。 我們會在下一節詳細討論佇列。
命令
另一種通訊互動類型是命令。 微服務可能需要其他微服務執行動作。 訂購微服務可能需要出貨微服務為核准的訂單安排出貨。 在圖 4-12 中,一項微服務 (產生者) 將訊息傳送給另一項微服務 (取用者),命令它執行作業。
圖 4-12. 以命令與佇列互動
通常,產生者不需要回應,而且可能會對訊息射後不理。 如果需要回復,取用者會另外將訊息傳回給另一個通道上的產生者。 命令訊息最好以非同步方式使用訊息佇列傳送。 輕量型訊息代理程式支援。 見上圖,請注意佇列如何分隔和分離這兩個服務。
由於許多訊息佇列可能會多次分派相同的訊息,稱為至少一次傳遞,因此取用者必須使用相關的 等冪訊息處理模式正確識別及處理這些案例。
訊息佇列是產生者和取用者傳遞訊息的媒介建構。 佇列實作非同步的點對點傳訊模式。 產生者知道何時需要傳送命令以及如何恰當路由。 佇列保證訊息只會由其中一個讀取通道的取用者執行個體處理。 在此案例中,產生者或取用者服務可以擴增,卻不會影響另一方。 此外,兩端的技術可能截然不同,這表示我們可能讓 Java 微服務呼叫 Golang 微服務。
在第 1 章中,我們討論了支援服務。 支援服務是雲端原生系統相依的輔助資源。 訊息佇列就是支援服務。 Azure 雲端支援以下兩種訊息佇列,供雲端原生系統用以實作命令傳訊:Azure 儲存體佇列和 Azure 服務匯流排佇列。
Azure 儲存體佇列
Azure 儲存體佇列提供簡單的佇列基礎結構,快速又經濟實惠,由 Azure 儲存體帳戶支援。
Azure 儲存體佇列具有可靠且可持續傳訊的 REST 型佇列機制。 它們提供最小的功能集,但很實惠,且可儲存數百萬則訊息。 容量最高可達 500 TB。 一則訊息的大小最高為 64 KB。
您可使用 HTTP 或 HTTPS 透過通過驗證的呼叫,存取來自世界各地的訊息。 儲存體佇列可以擴增為大量的並行用戶端,以因應流量尖峰。
換言之,此服務有所限制:
不保證訊息順序。
訊息只保留七天,時屆將自動移除。
不支援狀態管理、重複偵測或交易。
圖 4-13 顯示 Azure 儲存體佇列的階層。
圖 4-13. 儲存體佇列階層
見上圖,請注意儲存體佇列如何將訊息儲存在基礎 Azure 儲存體帳戶中。
Microsoft 為開發人員提供了數個用戶端和伺服器端程式庫,供儲存體佇列處理之用。 可支援大部分的主要平台,包括 .NET、Java、JavaScript、Ruby、Python 和 Go。 不建議開發人員直接與這些程式庫通訊。 這會緊密結合微服務程式碼和 Azure 儲存體佇列服務。 最好是隔離 API 的實作詳細資料。 引進仲介層 (即中繼 API),公開泛型作業並封裝具體程式庫。 這種鬆散結合可讓您交換佇列服務,但無須變更主線服務程式碼。
Azure 儲存體佇列是在雲端原生應用程式中實作命令傳訊的經濟實惠選項。 特別是當佇列大小超過 80 GB,或可接受簡單功能集時。 您只需要支付儲存訊息的費用,沒有固定的小時費率。
Azure 服務匯流排佇列
如需更複雜的傳訊需求,請考慮 Azure 服務匯流排佇列。
Azure 服務匯流排位於強固的訊息基礎結構上,支援代理傳訊模型。 訊息會可靠地儲存在訊息代理程式 (佇列) 中,直到為取用者接收為止。 佇列保證依先入先出 (FIFO) 原則傳遞訊息,遵守訊息新增至佇列的順序。
訊息的大小可更大,最高可達 256 KB。 訊息保存在佇列中,無時間限制。 服務匯流排不僅支援 HTTP 型呼叫,也提供對 AMQP 通訊協定的完整支援。 AMQP 是不同廠商間的開放標準,可支援二進位通訊協定和更高程度的可靠性。
服務匯流排提供一組豐富的功能,包括交易支援和重複偵測功能。 佇列保證每則訊息「最多只傳遞一次」。 它會自動捨棄已傳送過的訊息。 如果產生者不確定,可以重新傳送相同的訊息,但服務匯流排保證只會處理一個複本。 重複偵測可讓您不必建置額外的基礎結構管道。
另有兩個企業功能,分別是資料分割和工作階段。 傳統的服務匯流排佇列是由單一訊息代理程式處理,並儲存在單一訊息存放區中。 但是,服務匯流排資料分割會將佇列分散到多個訊息代理程式和訊息存放區。 整體輸送量不再受單一訊息代理程式或訊息存放區的效能所限。 此外,訊息存放區暫時中斷也不會造成分割的佇列無法使用。
服務匯流排工作階段則提供處理群組相關訊息的方式。 假設在某個工作流程中,有多則訊息必須一起處理,最後才完成作業。 您必須針對佇列明確啟用工作階段,而且每則相關的訊息都必須包含相同的工作階段識別碼,才能利用此優勢。
不過,有一些重要的注意事項:服務匯流排佇列大小限制為 80 GB,遠小於存放區佇列提供的大小。 此外,服務匯流排佇列會產生基本成本,而且是按作業次數收費。
圖 4-14 概述服務匯流排佇列的全面架構。
圖 4-14. 服務匯流排佇列
見上圖,請注意點對點的關聯性。 相同提供者的兩個執行個體會將訊息加入單一服務匯流排佇列。 每則訊息只會被右邊三個取用者執行個體其中之一取用。 接下來我們將討論,當不同的取用者可能全都對相同的訊息感興趣時,該如何實作傳訊。
事件
訊息佇列是實作通訊的有效方式,產生者可以非同步方式將訊息傳送給取用者。 不過,如果許多不同的取用者都對同一則訊息感興趣時,會發生什麼事? 每個取用者的專用訊息佇列無法妥善擴縮,而且會變得難以管理。
為處理這種情況,我們要討論第三種類型的訊息互動,事件。 一項微服務宣告某個動作已發生。 其他微服務如有興趣,則會回應該動作,即事件。 這就是事件驅動架構樣式。
事件驅動架構是兩個步驟的處理流程。 針對指定的狀態變更,微服務會將事件發佈至訊息代理程式,提供給任何其他感興趣的微服務。 感興趣的微服務只要在訊息代理程式中訂閱該事件,即可獲得通知。 您可以使用發佈/訂閱模式來實作事件型通訊。
圖 4-15 顯示購物籃微服務發佈了一個事件,而有另外兩項微服務訂閱此事件。
圖 4-15: 事件驅動傳訊
請注意位於通訊通道中間的事件匯流排元件。 這個自訂類別,可封裝訊息代理程式,並將它與基礎應用程式分離。 訂購和清查兩項微服務會在不知道彼此,也不知道購物籃微服務的狀況下,獨立操作事件。 當已註冊的事件被發佈至事件匯流排時,它們就會對其採取行動。
透過事件,我們將從佇列技術移至主題。 主題類似佇列,但支援一對多傳訊模式。 一項微服務發佈一則訊息。 多項訂閱微服務可以選擇接收並處理該訊息。 圖 4-16 顯示主題架構。
圖 4-16: 主題架構
在上圖中,發行者會將訊息傳送至主題。 最後,訂閱者會收到來自訂閱的訊息。 中間的主題會根據一組規則,將訊息轉送至暗藍色方塊的訂閱。 規則是將特定訊息轉送至訂閱的篩選條件。 在這裡,"GetPrice" 事件會被傳送至價格和記錄訂閱,因為記錄訂閱選擇接收所有訊息。 "GetInformation" 事件會被傳送至資訊和記錄訂閱。
Azure 雲端支援兩項不同的主題服務:Azure 服務匯流排主題和 Azure EventGrid。
Azure 服務匯流排主題
位於相同強固代理訊息模型上的 Azure 服務匯流排佇列是 Azure 服務匯流排主題。 主題可以接收多個獨立發行者的訊息,並將訊息傳送給最多 2,000 個訂閱者。 您可以在執行階段動態新增或移除訂閱,不需要停止系統或重新建立主題。
許多 Azure 服務匯流排佇列的進階功能也適用於主題,包括重複偵測和交易支援。 服務匯流排主題預設由單一訊息代理程式處理,而且會儲存在單一訊息存放區中。 但是,服務匯流排資料分割可將主題分散到許多訊息代理程式和訊息存放區,藉以擴縮主題。
排程的訊息傳遞會使用特定的處理時間標記訊息。 訊息不會在此時間前出現在主題中。 訊息延遲可讓您延後擷取訊息。 這兩者常用於作業依特定順序處理的工作流程處理案例中。 您可以延後處理接收到的訊息,直到前面的工作完成再處理。
服務匯流排主題是強固且經過證實的技術,可讓您在雲端原生系統中啟用發佈/訂閱通訊。
事件格線
雖然 Azure 服務匯流排是具有一套完整企業功能,通過對抗測試的訊息代理程式,但 Azure 事件方格是後起之秀。
乍看之下,事件方格就像是另一個主題型的傳訊系統。 但它在很多方面都不同。 其著重事件驅動工作負載,可啟用即時事件處理、深度 Azure 整合以及開放平台,全都使用無伺服器基礎結構。 專為現代雲端原生和無伺服器應用程式所設計
事件方格是集中式事件後擋板 (即管道),會回應 Azure 資源內及來自您服務的事件。
事件通知發佈至事件方格主題,接著再將每個事件路由到訂閱。 訂閱者對應至訂閱,並取用事件。 和服務匯流排一樣,事件方格支援篩選過的訂閱者模型,由訂閱設定規則接收想要的事件。 事件方格提供保證每秒 1 千萬個事件的快速輸送量,近乎達到即時傳遞,遠超過 Azure 服務匯流排能產生的輸送量。
事件方格的甜蜜點是與 Azure 基礎結構的網狀架構深入整合。 Cosmos DB 等 Azure 資源可以直接將內建事件發佈至其他感興趣的 Azure 資源,無須自訂程式碼。 事件方格可以從 Azure 訂閱、資源群組或服務發佈事件,讓開發人員更精細地控制雲端資源的生命週期。 不過,事件方格不限於 Azure。 它是一個開放平台,可取用從應用程式或協力廠商服務發佈的自訂 HTTP 事件,並將事件路由給外部訂閱者。
發佈及訂閱 Azure 資源的原生事件時,不需要撰寫任何程式碼。 您可以使用簡單的組態,將某 Azure 資源的事件整合到其他利用主題和訂閱內建管道的資源。 圖 4-17 顯示事件方格剖析。
圖 4-17: 事件方格剖析
EventGrid 和服務匯流排之間的主要差異是基礎訊息交換模式。
服務匯流排實作舊式的提取模型,由下游訂閱者主動輪詢主題訂閱以獲取新訊息。 此方法的優點是可讓訂閱者全權控制處理訊息的步調。 能控制在任何指定時間處理訊息的時機和數量。 未讀取的訊息會保留在訂閱中,直到處理為止。 明顯的缺點是事件產生時間與提取該訊息供訂閱者處理之輪詢作業時間的延遲。 此外,持續輪詢下一個事件的額外負荷會耗用資源和金錢。
EventGrid 則不同。 它實作推送模型,一收到事件即傳送至 EventHandlers,近乎即時傳遞事件。 它也會降低成本,因為只有在需要取用事件時才會觸發服務,而不是持續不斷地輪詢。 也就是說,事件處理常式必須處理傳入的負載並提供節流機制,以保護本身免於超過負荷。 許多取用這些事件的 Azure 服務,例如 Azure Functions 和 Logic Apps,會提供自動化的自動調整功能來處理增加的負載。
事件方格是完全受控的無伺服器雲端服務。 它會根據您的流量動態擴縮,並僅收取實際使用量的費用,您不用預先購買容量。 每個月的前 100,000 次作業免費:作業的定義是事件輸入 (傳入事件通知)、訂閱傳遞嘗試、管理呼叫以及依主體篩選。 EventGrid 提供 99.99% 的可用性,保證在 24 小時內傳遞事件,並使用內建的重試功能處理未成功的傳遞。 未傳遞的訊息會移至「無效信件」佇列供解析。 不同於 Azure 服務匯流排,事件方格可針對快速效能調整,而且不支援依序傳訊、交易和工作階段等功能。
在 Azure 雲端中串流訊息
Azure 服務匯流排和事件方格為公開單一離散事件的應用程式提供絕佳支援,例如新文件已插入 Cosmos DB 即屬此類事件。 但是,如果您的雲端原生系統需要處理相關事件的串流,該怎麼辦? 事件串流較為複雜。 它們一般會依時間排序、相互關聯,而且必須以群組的形式處理。
Azure 事件中樞是資料串流平台和事件擷取服務,可收集、轉換及儲存事件。 其可微調以擷取串流資料,例如從遙測內容發出的連續事件通知。 此服務的可擴縮程度相當高,而且每秒可以儲存及處理數百萬個事件。 如圖 4-18 所示,其通常是事件管線的 Front Door,會分離串流與事件取用。
圖 4-18. Azure 事件中樞
事件中樞支援低延遲和可設定的保留時間。 不同於佇列和主題,事件中樞會保留取用者讀取過的事件資料。 這項功能讓內部和外部的其他資料分析服務得以重新執行資料,供進一步分析。 儲存在事件中樞的事件只會在保留時間到期時刪除,預設為一天,但可設定。
事件中樞支援常見的事件發佈通訊協定,包括 HTTPS 和 AMQP。 也支援 Kafka 1.0。 現有的 Kafka 應用程式可與事件中樞通訊,,但需使用提供管理大型 Kafka 叢集之替代方案 Kafka 通訊協定。 許多開放原始碼雲端原生系統都採用 Kafka。
事件中樞透過分割的取用者模型實作訊息串流,在此模型中,每位取用者只會讀取訊息串流的特定子集 (即資料分割)。 此模式支援大幅的水平擴充來處理事件,並提供佇列和主題所沒有的其他串流功能。 分割區是事件中樞中保存的已排序事件序列。 當較新的事件送達時,系統會將它們加入序列的結尾。 圖 4-19 顯示事件中樞的資料分割。
圖 4-19. 事件中樞資料分割
每個取用者群組會讀取訊息串流的子集 (即資料分割),而不是讀取相同的資源。
對於必須串流大量事件的雲端原生應用程式而言,Azure 事件中樞會是強固且經濟實惠的解決方案。