在單一進程上執行的整合型應用程式中,元件會使用語言層級方法或函式呼叫來叫用彼此。 這些可以在您使用程式碼建立物件時(例如 new ClassName())緊密結合,或者在您透過相依性注入參考抽象而非具體物件實例時以分離方式叫用。 無論哪種方式,物件都在相同的進程中執行。 從整合型應用程式變更為微服務型應用程式時,最大的挑戰在於變更通訊機制。 從進程內方法呼叫直接轉換成對服務的 RPC 呼叫,會導致在分散式環境中執行不佳的聊天和沒有效率的通訊。 正確設計分散式系統的挑戰是眾所皆知的,甚至有一個稱為 分散式運算的謬誤 的準則,列出開發人員在從整合型設計移至分散式設計時經常做出的假設。
沒有一個解決方案,但有數個。 其中一個解決方案牽涉到盡可能隔離商務微服務。 然後,您會在內部的微服務之間使用非同步通訊,並以較粗粒度的通訊取代對象之間通常進行的細粒度通訊。 您可以將呼叫分組,並將匯總多個內部呼叫結果的數據傳回給用戶端,以執行此動作。
微服務型應用程式是在多個進程或服務上執行的分散式系統,通常甚至跨多部伺服器或主機執行。 每個服務實例通常是一個程式。 因此,服務必須使用 HTTP、AMQP 等進程間通訊協定或 TCP 等二進位通訊協議進行互動,視每個服務的性質而定。
微服務社群會提升「智慧端點和啞管」的理念。 此口號鼓勵在微服務之間進行盡可能解耦合的設計,而在單一微服務內則盡可能提高其凝聚力。 如先前所述,每個微服務都有自己的數據和自己的領域邏輯。 但是,撰寫端對端應用程式的微服務通常只是使用 REST 通訊進行編排,而不是使用 WS-* 和彈性事件驅動通訊等複雜通訊協定,而不是集中式的商務程式協調器。
這兩個常用的協定是 HTTP 請求/回應模式與資源 API(主要用於大部分的查詢),以及在多個微服務之間進行更新通訊時的輕量級非同步訊息傳遞。 下列各節會更詳細地說明這些內容。
通訊類型
用戶端和服務可以透過許多不同類型的通訊進行通訊,每個通訊都是以不同案例和目標為目標。 一開始,這些類型的通訊可以分類為兩個軸。
第一個座標軸會定義通訊協定是否為同步或異步:
同步通訊協定。 HTTP 是同步通訊協定。 用戶端會傳送要求,並等候來自服務的回應。 這與用戶端代碼的執行是否同步(即線程被封鎖)或異步(即線程未被封鎖且回應最終會到達回呼)無關。 這裡的重點是通訊協定 (HTTP/HTTPS) 是同步的,用戶端程式代碼只能在收到 HTTP 伺服器回應時繼續其工作。
異步通訊協定。 AMQP等其他通訊協定(許多作系統和雲端環境支援的通訊協定)會使用異步訊息。 用戶端程式代碼或訊息傳送者通常不會等候回應。 它將訊息傳送,就如同發送到 RabbitMQ 佇列或其他訊息代理時一樣。
第二個座標軸會定義通訊是否有單一接收者或多個接收者:
單一接收者。 每個要求都必須由一個接收者或服務處理。 此通訊的範例是 命令模式。
多個接收者。 每個要求可以由零到多個接收者處理。 這種類型的通訊必須是異步的。 例如,用於事件驅動架構等模式的發佈/訂閱機制。 這是在通過事件在多個微服務之間傳播數據更新時,基於事件總線介面或消息代理程式通過服務總線或類似的成品,例如Azure Service Bus來實作; 通常使用主題和訂閱。
微服務型應用程式通常會使用這些通訊樣式的組合。 最常見的類型是在叫用一般 Web API HTTP 服務時,使用例如 HTTP/HTTPS 的同步通訊協定進行單一接收者通訊。 微服務通常也會使用傳訊通訊協議進行微服務之間的異步通訊。
這些軸很值得瞭解,因此您可以清楚掌握可能的通訊機制,但在建置微服務時,它們不會是主要的考量。 用戶端線程執行的異步本質,或所選通訊協定的異步本質,都不是整合微服務時的要點。 重要的是能夠以異步方式整合微服務,同時維護微服務的獨立性,如下一節所述。
異步微服務的整合加強了微服務的自主性
如前所述,建置微服務型應用程式時的重要點是整合微服務的方式。 在理想情況下,您應該嘗試將內部微服務之間的通訊降到最低。 微服務之間的通訊越少越好。 但在許多情況下,您必須以某種方式整合微服務。 當您需要這樣做時,此處的重要規則是微服務之間的通訊應該是異步的。 這並不表示您必須使用特定的通訊協定(例如異步傳訊與同步 HTTP)。 這隻表示微服務之間的通訊應該只透過異步傳播數據來完成,但嘗試不要依賴其他內部微服務作為初始服務 HTTP 要求/回應作業的一部分。
盡量避免依賴多個微服務之間的同步通訊(要求/回應),即便是查詢也是如此。 每個微服務的目標是要自發且可供用戶端取用者使用,即使屬於端對端應用程式的其他服務已關閉或狀況不良。 如果您認為需要從一個微服務呼叫其他微服務(例如執行數據查詢的 HTTP 要求),才能提供用戶端應用程式的回應,則您有一個架構,當某些微服務失敗時將無法復原。
此外,在微服務之間具有 HTTP 相依性,例如使用 HTTP 要求鏈結建立長時間的要求 / 回應週期時,如圖 4-15 的第一個部分所示,不僅讓您的微服務不自發,而且其效能也會在鏈結中的其中一個服務未正常執行時受到影響。
在微服務之間新增同步相依性越多,例如查詢要求,用戶端應用程式的整體回應時間就越差。
圖 4-15。 微服務之間通訊的反模式和模式
如下圖所示,在同步通訊中,微服務之間會建立要求「鏈結」,同時提供用戶端要求。 這是反模式。 在異步通訊中,微服務使用異步訊息或 HTTP 輪詢與其他微服務進行通訊,但用戶端的請求會立即得到處理。
如果您的微服務需要在另一個微服務中引發其他動作,如果可能的話,請勿同步執行該動作,並作為原始微服務要求和回復作業的一部分。 相反地,請以異步方式執行(使用異步傳訊或整合事件、佇列等)。 但是,盡可能不要在原始同步請求和回應操作中同步調用該動作。
最後,當建構微服務時,最容易出現問題的情況就是如果你的初始微服務需要其他微服務所擁有的數據,切勿依賴同步請求這些數據。 相反地,使用最終一致性將該資料(只需您需要的屬性)複製或傳遞到初始服務的資料庫中(通常透過集成事件,如後續章節所述)。
如每個微服務區段的 識別領域模型界限 中所述,跨數個微服務複製某些數據並不是不正確的設計,相反地,當您這麼做時,您可以將數據轉譯成該額外領域或限定內容的特定語言或詞彙。 例如,在 eShopOnContainers 應用程式中 ,您有一 identity-api 個名為的微服務,負責大部分用戶的數據,且實體名為 User。 不過,當您需要在微服務中 Ordering 儲存使用者的相關數據時,請將它儲存為名為 Buyer的不同實體。 實體 Buyer 與原始 User 實體共用相同的身份,但它可能只有 Ordering 網域所需的少數屬性,而不是整個使用者檔案。
您可以使用任何通訊協定,以異步方式跨微服務通訊和傳播數據,以取得最終的一致性。 如前所述,您可以使用事件總線或訊息代理程式來使用整合事件,或甚至可以改為輪詢其他服務來使用 HTTP。 它並不重要。 重要的規則是不要在微服務之間建立同步相依性。
下列各節說明您可以考慮在微服務型應用程式中使用的多個通訊樣式。
通訊樣式
根據您想要使用的通訊類型而定,有許多通訊協定和選項可供通訊使用。 如果您使用同步要求/回應型通訊機制,HTTP 和 REST 方法等通訊協定是最常見的,特別是當您在 Docker 主機或微服務叢集外部發佈服務時。 如果您在內部服務之間通訊(在 Docker 主機或微服務叢集內),您可能也想要使用二進位格式通訊機制(例如使用 TCP 和二進位格式的 WCF)。 或者,您可以使用異步訊息式通訊機制,例如AMQP。
也有多個訊息格式,例如 JSON 或 XML,甚至二進位格式,這可以更有效率。 如果您選擇的二進位格式不是標準格式,則可能不是使用該格式公開發佈服務的好主意。 您可以使用非標準格式進行微服務之間的內部通訊。 您可以在 Docker 主機或微服務叢集內的微服務之間通訊時執行此動作(例如 Docker 協調器),或與微服務通訊的專屬用戶端應用程式。
使用 HTTP 和 REST 的要求/回應通訊
當用戶端使用要求/回應通訊時,它會將要求傳送至服務,然後服務會處理要求並傳回回應。 要求/回應通訊特別適合從用戶端應用程式查詢即時 UI(即時使用者介面)的數據。 因此,在微服務架構中,您可能會針對大部分的查詢使用此通訊機制,如圖 4-16 所示。
圖 4-16。 使用 HTTP 要求/回應通訊 (同步或異步)
當用戶端使用要求/響應通訊時,它會假設回應會以短時間內送達,通常少於一秒,或最多幾秒鐘。 針對延遲的回應,您必須根據 傳訊模式 和 傳訊技術實作異步通訊,這是我們在下一節中說明的不同方法。
要求/回應通訊的熱門架構樣式是 REST。 此方法是以 HTTP 通訊協定緊密結合為基礎,並採用 GET、POST 和 PUT 等 HTTP 動詞。 REST 是建立服務時最常使用的架構通訊方法。 您可以在開發 ASP.NET Core Web API 服務時實作 REST 服務。
使用 HTTP REST 服務作為介面定義語言時,會有額外的值。 例如,如果您使用 Swagger 元數據 來描述您的服務 API,您可以使用產生用戶端存根的工具,直接探索及取用您的服務。
其他資源
馬丁·福勒 理查森成熟度模型 REST 模型的描述。
https://martinfowler.com/articles/richardsonMaturityModel.html斯瓦格 官方網站。
https://swagger.io/
根據 HTTP 技術之推送和即時通訊
另一種可能性(通常與 REST 不同)是與較高層級架構的即時和一對多通訊,例如 ASP.NET SignalR 和 WebSocket 等通訊協定。
如圖 4-17 所示,即時 HTTP 通訊表示您可以讓伺服器程式代碼在數據可供使用時,將內容推送至已連線的用戶端,而不是讓伺服器等候用戶端要求新數據。
圖 4-17。 一對多即時異步訊息通訊
SignalR 是實現從後端伺服器將內容推送至用戶端的實時通訊的好方法。 由於通訊是即時的,用戶端應用程式幾乎會立即顯示變更。 這通常是由 WebSocket 之類的通訊協議來處理,使用許多 WebSocket 連線(每個用戶端一個)。 典型的範例是當服務同時將運動遊戲分數的變更傳達給許多用戶端應用程式時。