微服務之間的通信

在monolithic單體應用程序中,不同的組件之間通過編程語言級別的方法或函數調用相互調用。 相反,基於微服務的應用程序是在多臺計算機上運行的分佈式系統。 每個服務實例通常是一個進程。 因此,如下圖所示,服務必須使用進程間通信(IPC)機制進行交互。

In a microservices application, the services need an inter-process communication (IPC) mechanism (whereas modules in a monolith can call routines)

交互方式

爲服務選擇IPC機制時,首先考慮服務如何交互是必要的。 客戶端⇔服務交互方式多種多樣。 它們可以沿兩個維度進行分類。 第一個維度是互動是一對一還是一對多:

  • 一對一–每個客戶端請求僅由一個服務實例處理。
  • 一對多–每個請求由多個服務實例處理。

第二個維度是交互是同步還是異步:

  • 同步–客戶端期望服務及時響應,甚至在等待時可能會阻塞。
  • 異步-客戶端在等待響應時不會阻塞,並且響應(如果有的話)不一定會立即發送。

下表顯示了各種交互樣式。

One-to-One One-to-Many
Synchronous Request/response  — 
Asynchronous Notification Publish/subscribe
Request/async response Publish/async responses

存在以下類型的一對一交互:

  • Request/response–客戶端向服務發出請求並等待響應。客戶希望響應能夠及時到達。在基於線程的應用程序中,發出請求的線程甚至可能在等待時阻塞。
  • Notification (也稱爲單向one‑way 請求)–客戶端向服務發送請求,但不希望或未發送答覆。
  • Request/async response–客戶端將請求發送到服務,該服務以異步方式答覆。客戶端在等待時不會阻塞,並假設響應可能不會在一段時間內到達。

一對多互動有以下幾種:

  • Publish/subscribe–客戶端發佈通知消息,該消息由零個或更多感興趣的服務使用。
  • Publish/async responses –客戶端發佈請求消息,然後等待一定時間以等待感興趣的服務的響應。

每個服務通常使用這些交互樣式的組合。對於某些服務,單個IPC機制就足夠了。其他服務可能需要結合使用IPC機制。下圖顯示了當用戶請求一個旅行時,出租車服務應用程序中的服務如何交互。

A microservices-based taxi-hailing app can use a variety of communication methods: notification, request-response, publish-subscribe

服務使用notifications, request/response, 和publish/subscribe的組合。 例如,乘客的智能手機向行程管理服務發送通知以請求接機。 行程管理服務通過使用請求/響應調用旅客服務來驗證旅客的帳戶是否處於活動狀態。 然後,行程管理服務創建行程,並使用發佈/訂閱來通知其他服務,包括分派器,後者查找可用的驅動程序。

現在我們已經研究了交互樣式,讓我們看一下如何定義API。

定義 API

服務的API是服務與其客戶之間的contract。 無論選擇哪種IPC機制,使用某種接口定義語言(IDL)精確定義服務的API都是很重要的。 使用API優先方法定義服務甚至有很好的論據。 您可以通過編寫接口定義並與客戶開發人員一起審查來開始開發服務。 只有在對API定義進行迭代之後,您才能實現服務。 預先進行此設計會增加您建立滿足其客戶需求的服務的機會。

正如您將在本文後面看到的那樣,API定義的性質取決於您所使用的IPC機制。 如果使用messaging消息服務,則API包含消息通道和消息類型。 如果使用的是HTTP,則API由URL以及請求和響應格式組成。

調用API

服務的API始終會隨着時間而變化。在整體應用程序中,更改API和更新所有調用程序通常很簡單。在基於微服務的應用程序中,即使API的所有使用者都是同一應用程序中的其他服務,也要困難得多。通常,您無法強制所有客戶端與服務同步升級。另外,您可能會逐步部署服務的新版本,以便服務的舊版本和新版本將同時運行。制定處理這些問題的策略很重要。

處理API更改的方式取決於更改的大小。某些更改是次要的,並且與以前的版本向後兼容。例如,您可以將屬性添加到請求或響應。設計客戶和服務以使其遵循健壯性原則是有意義的。使用較舊API的客戶端應繼續使用新版本的服務。該服務爲缺少的請求屬性提供默認值,客戶端忽略任何額外的響應屬性。重要的是要使用IPC機制和消息傳遞格式,使您能夠輕鬆地開發API。

但是,有時您必須對API進行重大的,不兼容的更改。由於您無法強制客戶端立即升級,因此服務必須在一段時間內支持舊版API。如果使用的是基於HTTP的機制(例如REST),則一種方法是將版本號嵌入URL中。每個服務實例可能會同時處理多個版本。或者,您可以部署不同的實例,每個實例都處理特定的版本。

處理部分失敗

如有關API gateway的文章中我們提到了,在分佈式系統中,存在着永遠存在的部分故障風險。 由於客戶和服務是獨立的流程,因此服務可能無法及時響應客戶的請求。 服務可能由於故障或維護而關閉。 否則服務可能過載,並且對請求的響應非常緩慢。

例如,考慮該文章中的“產品詳細信息”方案。 假設推薦服務沒有響應。 客戶端的實現可能會無限期地阻塞等待響應。 這不僅會導致不良的用戶體驗,而且在許多應用程序中會消耗寶貴的資源,例如線程。 最終,運行時將耗盡線程並變得無響應,如下圖所示。

爲避免此問題,必須設計服務以處理部分故障。

Netflix所描述的是一種很好的遵循方法。處理部分故障的策略包括:

  • 網絡超時–永遠不會無限阻塞,並且在等待響應時始終使用超時。使用超時可確保資源不會無限期地被佔用。
  • 限制未完成請求的數量–限制客戶端可以使用特定服務的未完成請求的數量。如果已達到限制,則發出其他請求可能毫無意義,並且這些嘗試必須立即失敗。
  • 斷路器模式–跟蹤成功和失敗請求的數量。如果錯誤率超過配置的閾值,請使斷路器跳閘,以便進一步嘗試立即失敗。如果大量請求失敗,則表明該服務不可用,並且發送請求毫無意義。超時後,客戶端應重試,如果成功,則合上斷路器。
  • 提供回退–當請求失敗時執行回退邏輯。例如,返回緩存的數據或默認值,例如空的建議集。

Netflix Hystrix是實現這些模式和其他模式的開源庫。如果您使用的是JVM,則絕對應該考慮使用Hystrix。而且,如果您在非JVM環境中運行,則應使用等效的庫。

IPC Technologies

有很多不同的IPC技術可供選擇。服務可以使用基於請求/響應的同步通信機制,例如基於HTTP的REST或Thrift。或者,他們可以使用基於消息的異步通信機制,例如AMQP或STOMP。還有各種不同的消息格式。服務可以使用人類可讀的,基於文本的格式,例如JSON或XML。或者,他們可以使用二進制格式(效率更高),例如Avro或Protocol Buffers。稍後,我們將討論同步IPC機制,但首先讓我們討論異步IPC機制。

基於消息的異步通信

使用消息傳遞時,進程通過異步交換消息進行通信。客戶端通過向其發送消息來向服務發出請求。如果期望該服務進行答覆,則通過將單獨的消息發送回客戶端來進行答覆。由於通信是異步的,因此客戶端不會阻止等待答覆。而是編寫客戶端,假定不會立即收到答覆。

一條消息由標頭(例如發送方之類的元數據)和一條消息主體組成。消息通過通道交換。任何數量的生產者都可以將消息發送到一個頻道。同樣,任何數量的使用者都可以從頻道接收消息。有兩種渠道,點對點和發佈-訂閱。點對點通道將消息傳遞給正從該通道讀取的消費者中的一個。服務使用點對點渠道進行前面所述的一對一交互樣式。發佈訂閱通道將每個消息傳遞給所有附加的使用者。服務將發佈-訂閱通道用於上述一對多交互樣式。

下圖顯示了計程車應用程序如何使用發佈-訂閱通道。

Microservices in taxi-hailing application use publish-subscribe channels for communication between dispatcher and other services

行程管理服務通過將“行程創建”消息寫入發佈-訂閱頻道來通知感興趣的服務(例如,調度程序)有關新行程。分派器通過將“驅動程序建議”消息寫入發佈-訂閱通道來找到可用的驅動程序並通知其他服務。

有許多消息傳遞系統可供選擇。您應該選擇一種支持多種編程語言的語言。一些消息傳遞系統支持標準協議,例如AMQP和STOMP。其他消息傳遞系統具有專有但已記錄的協議。有很多開源消息傳遞系統可供選擇,包括RabbitMQ,Apache Kafka,Apache ActiveMQ和NSQ。在較高級別上,它們都支持某種形式的消息和通道。他們都努力做到可靠,高性能和可擴展。但是,每個經紀人的消息傳遞模型的詳細信息存在很大差異。

使用消息傳遞有很多優點:

  • 使客戶端與服務脫鉤–客戶端僅通過向適當的通道發送消息即可發出請求。客戶端完全不知道服務實例。它不需要使用發現機制來確定服務實例的位置。
  • 消息緩衝–使用同步請求/響應協議(例如HTTP),客戶端和服務在交換期間必須都可用。相反,消息代理將寫入通道的消息排隊,直到消費者可以處理它們爲止。例如,這意味着即使訂單履行系統很慢或不可用,在線商店也可以接受來自客戶的訂單。訂單消息只是排隊。
  • 靈活的客戶端-服務交互–消息支持前面描述的所有交互樣式。
  • 顯式進程間通信–基於RPC的機制試圖使調用遠程服務看起來與調用本地服務相同。但是,由於物理定律和部分失效的可能性,它們實際上是完全不同的。消息傳遞使這些差異非常明顯,因此開發人員不會陷入錯誤的安全感中。

但是,使用消息傳遞有一些缺點:

  • 額外的操作複雜性–郵件系統是又一個必須安裝,配置和操作的系統組件。郵件代理必須高度可用,否則會影響系統可靠性。
  • 實現基於請求/響應的交互的複雜性–請求/響應式的交互需要一些工作來實現。每個請求消息必須包含一個回覆通道標識符和一個相關標識符。服務將包含相關ID的響應消息寫入回覆通道。客戶端使用相關性ID將響應與請求進行匹配。使用直接支持請求/響應的IPC機制通常會更容易。

現在,我們已經研究了使用基於消息的IPC,下面我們來研究基於請求/響應的IPC。

Synchronous, Request/Response IPC 請求/響應的同步IPC

當使用基於請求/響應的同步IPC機制時,客戶端會將請求發送到服務。該服務處理請求併發送回響應。在許多客戶端中,發出請求的線程在等待響應時會阻塞。其他客戶端可能使用異步的,事件驅動的客戶端代碼,這些代碼可能由Futures或Rx Observables封裝。但是,與使用消息傳遞時不同,客戶端假定響應將及時到達。有許多協議可供選擇。兩種流行的協議是REST和Thrift。首先讓我們看一下REST。

REST
如今,以RESTful風格開發API已成爲一種時尚。 REST是(幾乎始終)使用HTTP的IPC機制。 REST中的一個關鍵概念是一種資源,通常代表一個業務對象,例如客戶或產品,或業務對象的集合。 REST使用HTTP謂詞來操縱資源,這些謂詞是使用URL引用的。例如,GET請求返回資源的表示形式,該形式可以是XML文檔或JSON對象的形式。 POST請求創建一個新資源,而PUT請求更新一個資源。

下圖顯示了出租車服務應用程序可能使用REST的方式之一。

In microservices-based taxi-hailing app, passenger smartphone sends POST request, which trip management microservice converts to GET request to passenger-verification microservice

乘客的智能手機通過向旅行管理服務的/ trips資源發出POST請求來請求旅行。該服務通過向乘客管理服務發送有關乘客信息的GET請求來處理該請求。在確認乘客被授權創建行程後,行程管理服務將創建行程並將201響應返回至智能手機。

許多開發人員聲稱他們基於HTTP的API是RESTful的。但是,正如Fielding在此博客文章中所描述的那樣,實際上並非全部。 Leonard Richardson(無關係)爲REST定義了一個非常有用的成熟度模型,該模型包含以下級別。

  • 級別0 –級別0 API的客戶端通過向其唯一的URL端點發出HTTP POST請求來調用服務。每個請求都指定要執行的操作,操作的目標(例如業務對象)以及任何參數。
  • 1級– 1級API支持資源的概念。爲了對資源執行操作,客戶端發出POST請求,該請求指定要執行的操作以及任何參數。
  • 級別2 –級別2 API使用HTTP謂詞執行操作:GET檢索,POST創建和PUT更新。請求查詢參數和主體(如果有)指定操作的參數。這使服務能夠利用Web基礎結構,例如爲GET請求進行緩存。
  • 3級– 3級API的設計基於極爲命名的HATEOAS(超文本作爲應用程序狀態引擎)原理。基本思想是,由GET請求返回的資源表示形式包含用於對該資源執行允許動作的鏈接。例如,客戶可以使用響應於發送來檢索訂單的GET請求而返回的Order表示中的鏈接來取消訂單。 HATEOAS的好處包括不再需要將URL硬編碼到客戶端代碼中。另一個好處是,由於資源的表示形式包含允許操作的鏈接,因此客戶端不必猜測在當前狀態下可以對資源執行哪些操作。

使用基於HTTP的協議有很多好處:

  • HTTP簡單而熟悉。
  • 您可以使用擴展程序(例如Postman)從瀏覽器中測試HTTP API,也可以使用curl(例如,使用JSON或其他某種文本格式)從命令行測試HTTP API。
  • 它直接支持請求/響應樣式的通信。
  • HTTP當然是防火牆友好的。
  • 它不需要中間代理,從而簡化了系統的體系結構。

使用HTTP有一些缺點:

  • 它僅直接支持交互的請求/響應樣式。您可以使用HTTP進行通知,但是服務器必須始終發送HTTP響應。
  • 因爲客戶端和服務直接通信(沒有中介來緩衝消息),所以它們必須在交換期間都處於運行狀態。
  • 客戶端必須知道每個服務實例的位置(即URL)。如上一篇有關API網關的文章所述,這是現代應用程序中的一個重要問題。客戶端必須使用服務發現機制來定位服務實例。

開發人員社區最近重新發現了RESTful API的接口定義語言的價值。有一些選項,包括RAML和Swagger。一些IDL(例如Swagger)允許您定義請求和響應消息的格式。其他(例如RAML)要求您使用單獨的規範(例如JSON Schema)。除了描述API之外,IDL通常還具有從接口定義生成客戶端存根和服務器框架的工具

Message Formats消息格式

現在,我們已經研究了HTTP和Thrift,現在讓我們檢查消息格式的問題。如果您使用的是消息傳遞系統或REST,則可以選擇消息格式。其他IPC機制(例如Thrift)可能僅支持少量消息格式,也許僅支持一種。無論哪種情況,都必須使用跨語言消息格式。即使您今天用一種語言編寫微服務,將來也可能會使用其他語言。

消息格式主要有兩種:文本和二進制。基於文本的格式的示例包括JSON和XML。這些格式的優點是它們不僅易於閱讀,而且是自描述的。在JSON中,對象的屬性由名稱/值對的集合表示。同樣,在XML中,屬性由命名的元素和值表示。這使消息的使用者可以選擇其感興趣的值,而忽略其餘值。因此,對消息格式的微小更改可以輕鬆地向後兼容。

XML文檔的結構由XML模式指定。隨着時間的流逝,開發人員社區逐漸意識到JSON也需要類似的機制。一種選擇是使用JSON Schema,它既可以獨立使用,也可以作爲IDL(例如Swagger)的一部分使用。

使用基於文本的消息格式的缺點是消息往往很冗長,尤其是XML。因爲消息是自描述的,所以每條消息除其值外還包含屬性的名稱。另一個缺點是解析文本的開銷。因此,您可能要考慮使用二進制格式。

總結

微服務必須使用進程間通信機制進行通信。 在設計服務如何通信時,您需要考慮各種問題:服務如何交互,如何爲每個服務指定API,如何擴展API以及如何處理部分故障。 微服務可以使用兩種IPC機制:異步消息傳遞和同步請求/響應。 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章