五分鐘了解微服務(wù)架構(gòu)通信模式
通信是微服務(wù)架構(gòu)中的關(guān)鍵要素,人們廣泛討論的焦點(diǎn)是如何選擇最有效的方法進(jìn)行服務(wù)間交互。在這篇文章中,將探討和總結(jié)微服務(wù)的最佳通信策略,深入探討如何有效利用每種通信方式。

交互方式
要有效理解微服務(wù)架構(gòu)中的服務(wù)通信方式,首先必須熟悉可用的交互方式。每種風(fēng)格都有其獨(dú)特的優(yōu)缺點(diǎn),在為服務(wù)確定最合適的通信機(jī)制的明智決定之前,全面了解這些細(xì)微差別至關(guān)重要。這些基礎(chǔ)知識(shí)可確保所選方法完全符合系統(tǒng)的具體要求和挑戰(zhàn)。
交互方式可以分為兩個(gè)維度,第一個(gè)維度是一對一還是一對多的交互:
- 一對一(One-to-one) - 每個(gè)客戶請求由一個(gè)服務(wù)處理。
 - 一對多(One-to-many) - 每個(gè)請求由多個(gè)服務(wù)處理。
 
第二個(gè)維度是同步還是異步交互。
- 同步(Synchronous) - 客戶端希望服務(wù)及時(shí)做出響應(yīng),甚至可能在等待時(shí)阻塞。
 - 異步(Asynchronous) - 客戶端不會(huì)阻塞,即使有響應(yīng),也不一定立即發(fā)送。
 
下表顯示了不同維度的組合:

通信維度
下面分別簡要介紹一下。
一對一交互:
- 請求/響應(yīng) - 客戶端向服務(wù)端提出請求并等待響應(yīng)??蛻舳讼M憫?yīng)能及時(shí)到達(dá),甚至可能在等待時(shí)阻塞。這種交互方式通常會(huì)導(dǎo)致服務(wù)緊耦合。
 - 異步請求/響應(yīng) - 客戶端向服務(wù)端發(fā)送請求,服務(wù)端以異步方式回復(fù)。客戶端在等待時(shí)不會(huì)阻塞,而服務(wù)端可能很長時(shí)間都不會(huì)發(fā)送響應(yīng)。
 - 單向通知 - 客戶端向服務(wù)端發(fā)送請求,但不期望立即獲得回復(fù)。
 
一對多交互:
- 發(fā)布/訂閱 - 客戶端發(fā)布一條通知消息,由零個(gè)或多個(gè)感興趣的服務(wù)消費(fèi)。
 - 發(fā)布/同步響應(yīng) - 客戶端發(fā)布請求信息,然后等待相關(guān)服務(wù)的響應(yīng)。
 
記住,一種服務(wù)可以有多種通信方式!
使用同步遠(yuǎn)程過程(Remote Procedure Invocation)調(diào)用模式進(jìn)行通信
客戶端向服務(wù)端發(fā)送請求,服務(wù)處理請求并發(fā)回響應(yīng)。有些客戶端可能會(huì)阻塞等待響應(yīng),有些客戶端則可能采用反應(yīng)式非阻塞架構(gòu)。但與使用消息傳遞不同的是,客戶端假定響應(yīng)會(huì)及時(shí)到達(dá)。
下圖顯示了 RPI 的工作原理。客戶端中的業(yè)務(wù)邏輯會(huì)調(diào)用 PRI 代理適配器類實(shí)現(xiàn)的代理接口,RPI 代理向服務(wù)發(fā)出請求。
請求由 RPI 服務(wù)端適配器類處理,該類通過接口調(diào)用服務(wù)的業(yè)務(wù)邏輯,然后將回復(fù)發(fā)送給 RPI 代理,后者將結(jié)果返回給客戶端的業(yè)務(wù)邏輯。
代理接口通常封裝了底層通信協(xié)議。我們將重點(diǎn)介紹最流行的 REST 和 gRPC 協(xié)議。

REST API
REST 的關(guān)鍵概念是資源,通常代表單個(gè)業(yè)務(wù)對象(如客戶或產(chǎn)品)或業(yè)務(wù)對象集合。REST 通過 HTTP 動(dòng)詞來操作資源,資源使用 URL 引用。例如,GET 請求返回資源的表示形式,通常是 XML 文檔或 JSON 對象,也可以使用二進(jìn)制等其他格式。POST 請求創(chuàng)建新資源,PUT 請求更新資源。
1.REST API 的挑戰(zhàn):
(1) 在一次請求中獲取多個(gè)資源:
REST 資源通常以客戶和訂單等業(yè)務(wù)對象為重點(diǎn),這給在一次請求中獲取多個(gè)相關(guān)對象帶來了挑戰(zhàn)。例如,獲取訂單及其關(guān)聯(lián)的客戶通常需要多次 API 調(diào)用。常見的解決方法是增強(qiáng)應(yīng)用程序接口,使客戶端可以在一次調(diào)用中獲取相關(guān)資源,例如使用帶有擴(kuò)展查詢參數(shù)的 GET 請求來指定相關(guān)資源。雖然這種方法在很多情況下都很有效,但實(shí)施起來可能會(huì)很復(fù)雜、很耗時(shí),這也是 GraphQL 等用于更簡化數(shù)據(jù)檢索的替代技術(shù)興起的原因之一。
(2) 將操作映射到 HTTP 動(dòng)詞
一個(gè)值得注意的 REST API 設(shè)計(jì)挑戰(zhàn)是如何將業(yè)務(wù)對象上的特定操作分配給正確的 HTTP 動(dòng)詞。例如,更新訂單可能涉及取消或修改訂單等各種操作,而且并非所有更新都必須是冪等的,而這正是使用 HTTP PUT 方法所必需的。一種常見的方法是為不同的更新操作創(chuàng)建子資源,例如使用 POST 取消訂單(POST /orders/{orderId}/cancel )或修改訂單(POST /orders/{orderId}/revise )。另一種方法是將操作作為 URL 查詢參數(shù)。不過,這些方法可能并不完全符合 REST 原則。將操作映射到 HTTP 動(dòng)詞上的這種困難,促成了 gRPC 等替代技術(shù)的流行。
使用 REST 有很多好處:
- 使用簡單,大部分工程師都比較熟悉。
 - 可以在瀏覽器中使用 Postman 插件等工具測試 HTTP API,也可以在命令行中使用 curl 進(jìn)行測試(假設(shè)使用的是 JSON 或其他文本格式)。
 - 直接支持請求/響應(yīng)式通信。
 - HTTP 對防火墻是友好的。
 - 不需要中間代理,從而簡化了系統(tǒng)架構(gòu)。
 
使用 REST 有一些缺點(diǎn):
- 只支持請求/響應(yīng)式通信。
 - 可用性低。由于客戶端和服務(wù)端直接通信,沒有中間組件緩沖信息,因此在交互過程中,客戶端和服務(wù)端必須同時(shí)運(yùn)行。
 - 客戶端必須知道服務(wù)端實(shí)例的位置(URL)。在現(xiàn)代應(yīng)用中,這是一個(gè)非同小可的問題。客戶端必須使用所謂的服務(wù)發(fā)現(xiàn)機(jī)制來定位服務(wù)實(shí)例。
 - 在一次請求中獲取多個(gè)資源具有挑戰(zhàn)性。
 - 有時(shí)很難將多個(gè)更新操作映射到 HTTP 動(dòng)詞。
 
3.使用 gRPC
gRPC 提供了另一種選擇,它使用基于二進(jìn)制消息的協(xié)議,強(qiáng)調(diào) API 優(yōu)先的方法。gRPC 采用協(xié)議緩沖區(qū)(Protobuf),一種由谷歌開發(fā)的語言中立的序列化系統(tǒng),允許開發(fā)人員在基于Protobuf的接口定義語言(IDL)中定義 API。gRPC API 在 HTTP/2 上運(yùn)行,支持簡單的請求/響應(yīng)和流式 RPC,因此服務(wù)端可以向客戶端發(fā)送消息流,反之亦然。該技術(shù)支持創(chuàng)建定義明確的服務(wù)接口和強(qiáng)類型方法,為處理微服務(wù)架構(gòu)中各種復(fù)雜的通信模式提供了強(qiáng)大的框架。
gRPC 的優(yōu)點(diǎn)和缺點(diǎn)
gRPC 有幾個(gè)好處:
- 設(shè)計(jì)一個(gè)擁有豐富更新操作的應(yīng)用程序接口非常簡單。
 - 具有高效、緊湊的 IPC 機(jī)制,尤其是在交換大型信息時(shí)。
 - 雙向數(shù)據(jù)流可實(shí)現(xiàn) RPI 和消息傳遞兩種通信方式。
 - 可實(shí)現(xiàn)客戶端與用多種語言編寫的服務(wù)之間的互操作性。
 
gRPC 也有若干缺點(diǎn):
- 與基于 REST/JSON 的應(yīng)用程序接口相比,JavaScript 客戶端在使用基于 gRPC 的應(yīng)用程序接口時(shí)需要花費(fèi)更多的時(shí)間。
 - 老式防火墻可能不支持 HTTP/2。
 - gRPC 是 REST 的一個(gè)令人信服的替代方案,但與 REST 一樣,它也是一種同步通信機(jī)制,因此也存在部分失效的問題。
 
使用異步消息傳遞模式進(jìn)行通信
使用消息傳遞時(shí),服務(wù)通過異步消息進(jìn)行通信。基于消息傳遞的應(yīng)用程序通常使用消息代理,作為服務(wù)之間的中介??蛻舳送ㄟ^發(fā)送消息向服務(wù)端發(fā)出請求,如果服務(wù)端實(shí)例需要回復(fù),就會(huì)向客戶端發(fā)送一條單獨(dú)的消息。由于通信是異步的,客戶端不會(huì)阻塞等待回復(fù)。相反,客戶端在編寫時(shí)假定不會(huì)立即收到回復(fù)。
1.消息傳遞概述
摘自 Gregor Hohpe 和 Bobby Woolf 合著的《企業(yè)集成模式》一書:
消息通過信道進(jìn)行交互。發(fā)送方(應(yīng)用程序或服務(wù))向信道寫入消息,接收方(應(yīng)用程序或服務(wù))從信道讀取消息。我們先介紹下信息,再了解下信道。
2.關(guān)于消息
消息由消息頭(header) 和消息體(message body) 組成。
消息頭是描述發(fā)送數(shù)據(jù)的名-值對和元數(shù)據(jù)的集合。除了表示消息發(fā)送方的名-值對,消息頭還包含其他名-值對,如由發(fā)送方或消息基礎(chǔ)設(shè)施生成的唯一消息 ID,以及可選的返回地址,該地址指定了應(yīng)寫入響應(yīng)的信道。消息體是以文本或二進(jìn)制格式發(fā)送的數(shù)據(jù)。
有幾種不同的消息:
- 文檔(Document) - 僅包含數(shù)據(jù)的通用消息,由接收方?jīng)Q定如何解釋。對命令的回復(fù)就是文檔消息的一個(gè)例子。
 - 命令(Command) - 相當(dāng)于 RPC 請求的消息,指定了要調(diào)用的操作及其參數(shù)。
 - 事件(Event) - 表示發(fā)件人發(fā)生了值得注意的事情的信息。事件通常是領(lǐng)域事件,表示領(lǐng)域?qū)ο螅ㄈ缬唵位蚩蛻簦┑臓顟B(tài)變化。
 
本文將主要介紹命令和事件。
3.關(guān)于信道
發(fā)送方的業(yè)務(wù)邏輯調(diào)用發(fā)送端接口,該接口封裝了底層通信機(jī)制。發(fā)送端由消息發(fā)送適配器類實(shí)現(xiàn),該適配器類通過信道向接收者發(fā)送消息。信道是消息傳送基礎(chǔ)架構(gòu)的一個(gè)抽象概念。接收器中的消息處理適配器類被調(diào)用來處理消息,并調(diào)用由消費(fèi)者業(yè)務(wù)邏輯實(shí)現(xiàn)的接收端接口。任何數(shù)量的發(fā)送者都可以向同一個(gè)信道發(fā)送消息。同樣,任何數(shù)量的接收者都可以從同一個(gè)信道接收消息。

信道基礎(chǔ)設(shè)施
了解兩種信道非常重要:點(diǎn)對點(diǎn)(Point-To-Point) 和發(fā)布-訂閱(Publish-Subscribe)。
- 點(diǎn)對點(diǎn)信道將消息準(zhǔn)確發(fā)送給正從信道讀取消息的消費(fèi)者之一。服務(wù)使用點(diǎn)對點(diǎn)信道來實(shí)現(xiàn)前面介紹的一對一交互方式。例如,命令消息通常通過點(diǎn)對點(diǎn)信道發(fā)送。
 - 發(fā)布-訂閱信道將每條消息發(fā)送給所有訂閱的消費(fèi)者。服務(wù)使用發(fā)布-訂閱信道來實(shí)現(xiàn)前面介紹的一對多交互方式。例如,事件消息通常通過發(fā)布-訂閱信道發(fā)送。
 
既然我們已經(jīng)清楚了解了異步通信,包括消息和信道的概念,那么接下來就應(yīng)該探索異步通信框架提供的各種通信機(jī)制的實(shí)現(xiàn)。
4.實(shí)現(xiàn)請求/響應(yīng)和異步請求/響應(yīng)
當(dāng)客戶端和服務(wù)端使用請求/響應(yīng)或異步請求/響應(yīng)進(jìn)行交互時(shí),客戶端發(fā)送請求,而服務(wù)端則返回響應(yīng)。這兩種交互方式的區(qū)別在于,使用請求/響應(yīng)時(shí),客戶端希望服務(wù)立即做出響應(yīng),而使用異步請求/響應(yīng)時(shí)則沒有這種期望。消息傳遞本質(zhì)上是異步的,因此只提供異步請求/響應(yīng)。但客戶端可以阻塞,直到收到響應(yīng)為止。
客戶端和服務(wù)端通過交換消息來實(shí)現(xiàn)異步請求/響應(yīng)式交互。如圖所示,客戶端向服務(wù)端對應(yīng)的點(diǎn)對點(diǎn)消息傳遞信道發(fā)送一條命令消息,其中指定了要執(zhí)行的操作及其參數(shù)。服務(wù)處理請求并向客戶端擁有的點(diǎn)對點(diǎn)信道發(fā)送包含結(jié)果的回復(fù)消息。

5.異步請求/響應(yīng)
從上圖可以看出,客戶端必須告訴服務(wù)端將響應(yīng)發(fā)送到哪兒,并且必須將響應(yīng)與請求匹配起來。幸運(yùn)的是,解決這兩個(gè)問題并不難。客戶端發(fā)送的命令報(bào)文帶有回復(fù)信道頭reply channel header。服務(wù)端將響應(yīng)消息寫入回復(fù)信道,響應(yīng)消息包含與命令消息標(biāo)識(shí)符具有相同值的關(guān)聯(lián) ID correlation id??蛻舳送ㄟ^correlation id將響應(yīng)消息與請求匹配。
由于客戶端和服務(wù)端使用消息傳遞進(jìn)行通信,因此本質(zhì)上是異步交互。理論上,客戶端可以阻塞直到收到響應(yīng),但實(shí)際上,客戶端會(huì)異步處理。此外,響應(yīng)通常由客戶端的任意實(shí)例進(jìn)行處理。
6.實(shí)現(xiàn)單向通知
使用異步消息傳遞可以直接實(shí)現(xiàn)單向通知??蛻舳讼蚍?wù)端對應(yīng)的點(diǎn)對點(diǎn)信道發(fā)送消息,通常是命令消息。服務(wù)端訂閱該信道并處理消息,但不發(fā)送回復(fù)??梢詮?fù)用"異步請求/響應(yīng)"相同的圖示,但沒有回復(fù)信道。
7.實(shí)現(xiàn)發(fā)布/訂閱
客戶端向發(fā)布-訂閱信道發(fā)布消息,多個(gè)消費(fèi)者可以讀取該消息。服務(wù)通過發(fā)布/訂閱來發(fā)布域事件,這些事件代表了對域?qū)ο蟮母?。發(fā)布域事件的服務(wù)擁有一個(gè)發(fā)布-訂閱信道,該通道的名稱源自域類。對特定域?qū)ο笫录信d趣的服務(wù)只需訂閱相應(yīng)的信道即可。

發(fā)布/訂閱
8.實(shí)現(xiàn)發(fā)布/同步響應(yīng)
發(fā)布/同步響應(yīng)交互方式是一種更高級別的交互方式,通過結(jié)合發(fā)布/訂閱和請求/響應(yīng)的元素來實(shí)現(xiàn)??蛻舳讼虬l(fā)布-訂閱信道發(fā)布一條指定了回復(fù)信道頭reply channel header的消息。消費(fèi)者向回復(fù)信道寫入包含correlation id的回復(fù)信息??蛻舳送ㄟ^correlation id將回復(fù)消息與請求匹配起來。
應(yīng)用程序中具有異步 API 的每個(gè)服務(wù)都將使用其中一種或多種實(shí)現(xiàn)技術(shù)。使用異步 API 調(diào)用操作的服務(wù)將有一個(gè)用于請求的消息信道。同樣,發(fā)布事件的服務(wù)也會(huì)將事件發(fā)布到事件消息信道。
使用消息代理
基于消息傳遞的應(yīng)用程序通常會(huì)使用消息代理(一種基礎(chǔ)架構(gòu)服務(wù),服務(wù)通過它進(jìn)行通信)。但基于代理的架構(gòu)并不是唯一的消息傳遞架構(gòu),也可以使用無代理消息傳遞架構(gòu),在這種架構(gòu)中,服務(wù)之間可以直接通信(本文將不涉及這一主題)。
1.基于代理的消息傳遞概述
消息代理是所有消息流動(dòng)的中介。發(fā)送者將消息寫入消息代理,然后由消息代理將消息發(fā)送給接收者。使用消息代理的主要好處是,發(fā)送者不需要知道消費(fèi)者的網(wǎng)絡(luò)位置。另一個(gè)好處是,消息代理可以緩沖消息,直到消費(fèi)者能夠處理。
有許多消息代理可供選擇。流行的開源消息代理包括以下幾種:
- ActiveMQ
 - RabbitMQ
 - Apache Kafka
 
每個(gè)代理都會(huì)做出不同的權(quán)衡。例如,延遲極低的代理可能不保留排序,不保證傳遞消息,只將消息存儲(chǔ)在內(nèi)存中;而保證傳遞消息并可靠的將消息存儲(chǔ)在磁盤上的代理可能會(huì)有更高的延遲。
哪種消息代理最合適取決于應(yīng)用程序的需求,應(yīng)用程序的不同部分甚至可能有不同的消息傳遞需求。
2.使用消息代理實(shí)現(xiàn)消息信道
每個(gè)消息代理都以不同的方式實(shí)現(xiàn)消息信道概念。如表所示,JMS 消息代理(如 ActiveMQ)有隊(duì)列和主題。基于 AMQP 的消息代理(如 RabbitMQ)有交換和隊(duì)列。Apache Kafka 有主題,AWS Kinesis 有流,AWS SQS 有隊(duì)列。 此外,一些消息代理提供比本章所述消息和信道抽象更靈活的消息傳遞機(jī)制。
消息代理列表
這里介紹的幾乎所有消息代理都支持點(diǎn)對點(diǎn)和發(fā)布-訂閱信道。AWS SQS 是個(gè)例外,它只支持點(diǎn)對點(diǎn)信道。
消息代理的問題
1.接收競爭和消息排序
挑戰(zhàn)之一保持消息有序的同時(shí)擴(kuò)展消息接收器。為了并發(fā)處理消息,通常需要多個(gè)服務(wù)實(shí)例。此外,即使是單個(gè)服務(wù)實(shí)例也可能會(huì)使用線程來并發(fā)處理多個(gè)消息。使用多線程和多服務(wù)實(shí)例并發(fā)處理消息可以提高應(yīng)用程序吞吐量,但并發(fā)處理消息的挑戰(zhàn)在于確保每條消息都能按順序處理。
例如,假設(shè)有三個(gè)服務(wù)實(shí)例從同一個(gè)點(diǎn)對點(diǎn)信道讀取消息,發(fā)送方按順序發(fā)布 "創(chuàng)建訂單"、"更新訂單"和"取消訂單"事件消息。簡單的消息傳遞實(shí)現(xiàn)可以同時(shí)將每條消息傳遞給不同的接收方。由于網(wǎng)絡(luò)問題或垃圾回收導(dǎo)致的延遲,消息的處理順序可能會(huì)被打亂,從而導(dǎo)致奇怪的行為。理論上,一個(gè)服務(wù)實(shí)例可能會(huì)在另一個(gè)服務(wù)處理"訂單創(chuàng)建"消息之前處理"訂單取消"消息!
Apache Kafka 和 AWS Kinesis 等現(xiàn)代消息代理常用的解決方案是使用分片(分區(qū))信道,下圖顯示了其工作原理。該解決方案分為三個(gè)部分:
- 分片信道由兩個(gè)或多個(gè)分片組成,每個(gè)分片的行為都和信道一樣。
 - 發(fā)送者在信息頭中指定分片key,通常是任意字符串或字節(jié)序列。消息代理通過分片key將信息分配給特定分片/分區(qū)。例如,可以通過計(jì)算分片key的哈希值乘以分片數(shù)來選擇分片。
 - 消息代理將接收器的多個(gè)實(shí)例分組,并將它們視為同一個(gè)邏輯接收器。例如,Apache Kafka 使用消費(fèi)者組(consumer group)一詞。消息代理將每個(gè)分片分配給一個(gè)接收器。當(dāng)接收器啟動(dòng)和關(guān)閉時(shí),會(huì)重新分配分片。
 

分片信道架構(gòu)
在本例中,每個(gè)訂單事件消息都以orderId作為其分片key。特定訂單的每個(gè)事件都發(fā)布到同一個(gè)分片,由單個(gè)消費(fèi)者實(shí)例讀取。因此,可以保證這些消息按順序處理。
2.處理重復(fù)消息
使用消息傳遞時(shí)必須解決的另一個(gè)難題是處理重復(fù)消息。理想情況下,消息代理應(yīng)該每條消息只傳遞一次(exactly-once),但保證準(zhǔn)確傳遞一次消息通常成本太高。相反,大多數(shù)消息代理都承諾至少傳遞一次消息(at-least-once)。
當(dāng)系統(tǒng)正常運(yùn)行時(shí),保證至少交付一次的消息代理只交付一次消息。然而,客戶端、網(wǎng)絡(luò)或消息代理的故障可能導(dǎo)致消息被傳送多次。假設(shè)一個(gè)客戶端處理了消息并更新了數(shù)據(jù)庫,但在發(fā)送確認(rèn)消息之前崩潰了。消息代理將再次傳送未確認(rèn)的消息,要么在該客戶端重啟時(shí)傳送給它,要么傳送給該客戶端的另一個(gè)副本。
理想情況下,應(yīng)該使用在重新傳遞消息時(shí)保留排序的消息代理。
假設(shè)客戶端在處理"創(chuàng)建訂單"事件后,又處理了同一訂單的"取消訂單"事件,而 "創(chuàng)建訂單"事件沒有得到確認(rèn)。消息代理應(yīng)同時(shí)重新傳遞"創(chuàng)建訂單"和"取消訂單"事件。如果只重新傳遞"創(chuàng)建訂單"事件,客戶機(jī)可能會(huì)取消訂單。
有幾種不同的方法可以處理重復(fù)信息:
- 編寫冪等消息處理程序。
 - 跟蹤并丟棄重復(fù)消息。
 
我們來簡單了解一下每種方案。
3.編寫冪等消息處理程序
如果處理消息的應(yīng)用邏輯是冪等的,那么重復(fù)報(bào)文是無害的。如果用相同的輸入值多次調(diào)用應(yīng)用邏輯不會(huì)產(chǎn)生額外的效果,那么應(yīng)用邏輯就是冪等的。例如,取消一個(gè)已經(jīng)取消的訂單就是一個(gè)冪等操作。使用客戶提供的 ID 創(chuàng)建訂單也是如此。
只要消息代理在重新傳遞消息時(shí)保留排序,冪等消息處理程序就可以安全執(zhí)行多次。
遺憾的是,應(yīng)用邏輯往往不是等效的。或者消息代理在重新傳遞消息時(shí)不保留順序,重復(fù)或失序的消息可能會(huì)導(dǎo)致錯(cuò)誤。在這種情況下,就必須自己編寫消息處理程序來跟蹤消息并丟棄重復(fù)消息。
4.跟蹤并丟棄重復(fù)消息
例如,考慮一個(gè)對消費(fèi)者信用卡進(jìn)行鑒權(quán)的消息處理程序,必須對每張訂單的信用卡精確鑒權(quán)一次。這個(gè)應(yīng)用程序邏輯示例每次調(diào)用都會(huì)產(chǎn)生不同的效果。如果重復(fù)的消息導(dǎo)致消息處理程序多次執(zhí)行這一邏輯,應(yīng)用程序的行為就會(huì)不正確。執(zhí)行這種應(yīng)用程序邏輯的消息處理程序必須通過檢測和丟棄重復(fù)消息來獨(dú)立實(shí)現(xiàn)。一個(gè)簡單的解決方案是,消息消費(fèi)者使用消息 ID 跟蹤已處理的消息,并丟棄任何重復(fù)消息。舉例來說,可以在數(shù)據(jù)庫表中存儲(chǔ)它處理過的每條消息的ID。下圖顯示了如何使用專用表來實(shí)現(xiàn)這一功能。

跟蹤消息流
消費(fèi)者處理消息時(shí),會(huì)在數(shù)據(jù)庫表中記錄message id,作為創(chuàng)建和更新業(yè)務(wù)實(shí)體的事務(wù)的一部分。在本例中,消費(fèi)者向 PROCESSED_MESSAGES 表中插入一條包含message id的記錄。如果是重復(fù)消息,則 INSERT 將失敗,用戶可以丟棄該消息。
另一種方法是讓消息處理程序在應(yīng)用程序表而不是專用表中記錄message id。這種方法在使用 NoSQL 數(shù)據(jù)庫時(shí)特別有用,因?yàn)?NoSQL 數(shù)據(jù)庫的事務(wù)模型有限,不支持將更新兩個(gè)表作為數(shù)據(jù)庫事務(wù)的一部分。
結(jié)論
總之,微服務(wù)架構(gòu)中通信方式的選擇對于應(yīng)用程序的整體效率和可擴(kuò)展性至關(guān)重要。在本文中,我們探討了從同步調(diào)用到異步消息傳遞等各種通信機(jī)制,每種機(jī)制都有其獨(dú)特的優(yōu)勢和合適的使用場景。正如我們所看到的,正確的通信策略不僅能提高性能,還能確保服務(wù)交互的彈性和靈活性。
在選擇通信方式時(shí),必須考慮服務(wù)交互的性質(zhì)、對實(shí)時(shí)數(shù)據(jù)的需求以及所涉及服務(wù)的復(fù)雜性等因素。請記住,我們的目標(biāo)是建立一個(gè)強(qiáng)大架構(gòu),使其能夠隨著組織需求和技術(shù)進(jìn)步而發(fā)展。















 
 
 









 
 
 
 