每日分享最新,最流行的軟件開發(fā)知識與最新行業(yè)趨勢,希望大家能夠一鍵三連,多多支持,跪求關(guān)注,點贊,留言。
如何使用 Apache Kafka 實現(xiàn)請求-響應(yīng)消息交換模式,優(yōu)缺點,以及與 CQRS 和事件溯源的比較。
如何與 Apache Kafka 進行請求-響應(yīng)通信?這是我經(jīng)常收到的最常見的問題之一。這篇博文探討了何時(不)使用這種消息交換模式、同步和異步通信之間的區(qū)別、與 CQRS 和事件溯源相比的優(yōu)缺點,以及如何在數(shù)據(jù)流基礎(chǔ)架構(gòu)中實現(xiàn)請求-響應(yīng)。
Apache Kafka 數(shù)據(jù)流中的消息隊列模式
在我開始這篇文章之前,我想讓你知道這個內(nèi)容是關(guān)于“JMS、消息隊列和 Apache Kafka”的博客系列的一部分:
- JMS 消息代理與 Apache Kafka 數(shù)據(jù)流的10 個比較標(biāo)準(zhǔn)
- 通過Apache Kafka 中的死信隊列 (DQL)進行錯誤處理的替代方案
- 這篇文章——使用 Apache Kafka實現(xiàn)請求-回復(fù)模式
- 即將推出——用于選擇正確消息系統(tǒng)的決策樹(JMS 與 Apache Kafka)
- 即將推出——從 JMS 消息代理到 Apache Kafka:集成、遷移和/或替換
我會盡快在此處鏈接其他帖子。
什么是請求-響應(yīng)(Request-reply)消息交換模式?
請求-響應(yīng)(有時稱為請求-答復(fù))是計算機在網(wǎng)絡(luò)中用于相互通信的主要方法之一。
第一個應(yīng)用程序發(fā)送一些數(shù)據(jù)的請求。第二個應(yīng)用程序響應(yīng)請求。它是一種消息交換模式,其中請求者向回復(fù)者系統(tǒng)發(fā)送請求消息,回復(fù)者系統(tǒng)接收并處理請求,最終返回消息作為響應(yīng)。
請求-回復(fù)效率低下,并且根據(jù)用例可能會遭受很多延遲。HTTP 或更好的 gRPC 適用于某些用例。請求-回復(fù) 被 CQRS(命令和查詢責(zé)任分離)模式“替換”為流數(shù)據(jù)的 Kafka。CQRS 在 JMS API 中是不可能的,因為 JMS 不提供狀態(tài)功能并且缺乏事件溯源功能。讓我們更深入地研究這些陳述。
請求-響應(yīng) (HTTP) 與數(shù)據(jù)流 (Kafka)
在討論同步和異步通信之前,讓我們探討一下請求-響應(yīng)和數(shù)據(jù)流背后的概念。傳統(tǒng)上,這是兩種不同的范式:
請求-響應(yīng) (HTTP):
- 通常是同步的
- 點對點
- 高延遲(與數(shù)據(jù)流相比)
- 預(yù)定義 API
- 連續(xù)加工
- 通常是異步的
- 事件驅(qū)動
- 低延遲
- 通用事件
大多數(shù)架構(gòu)需要點對點通信(例如,服務(wù)器和移動應(yīng)用程序之間)的請求-響應(yīng)和連續(xù)數(shù)據(jù)處理的數(shù)據(jù)流。考慮到這一點,讓我們看看 HTTP 與 Kafka 一起使用的用例。
同步與異步通信
請求-響應(yīng)消息交換模式通常是純粹同步實現(xiàn)的。然而,請求-響應(yīng)也可以異步實現(xiàn),響應(yīng)在某個未知的稍后時間返回。
讓我們看一下最流行的消息交換示例:REST、消息隊列和數(shù)據(jù)流。
同步 Restful API (HTTP)
Web 服務(wù)是應(yīng)用程序開發(fā)和企業(yè)應(yīng)用程序集成中同步通信背后的主要技術(shù)。雖然多年前 WSDL 和 SOAP 占主導(dǎo)地位,但REST/HTTP 是當(dāng)今幾乎所有 Web 服務(wù)中的通信標(biāo)準(zhǔn)。
我不會在這篇文章中討論“HTTP 與 REST”的爭論。簡而言之,REST(Representational state transfer)已被整個軟件行業(yè)所采用,并且是一套被廣泛接受的用于創(chuàng)建無狀態(tài)、可靠的 Web API 的指南。遵循 REST 約束的 Web API 被非正式地描述為 RESTful。RESTful Web API 通常松散地基于 HTTP 方法。
通過 HTTP 進行的同步 Web 服務(wù)調(diào)用保持連接打開并等待響應(yīng)傳遞或超時期限到期。
HTTP Web 服務(wù)的延遲比較高。使用 HTTP 時,它需要為每個請求-響應(yīng)迭代設(shè)置和斷開 TCP 連接。需要明確的是:對于許多用例來說,延遲仍然足夠好。
另一個可能的缺點是 HTTP 請求可能會阻塞等待隊列頭請求被處理,并且如果有太多未完成的 HTTP 請求,可能需要在服務(wù)器上設(shè)置 HTTP 斷路器。
異步消息隊列(IBM MQ、RabbitMQ)
消息隊列范式是發(fā)布者/訂閱者設(shè)計模式的兄弟,通常是更廣泛的面向消息的中間件系統(tǒng)的一部分。大多數(shù)消息傳遞系統(tǒng)在其 API 中同時支持發(fā)布者/訂閱者和消息隊列模型,例如 JAVA 消息服務(wù) (JMS)。如果您不熟悉此討論,請閱讀“ JMS 消息隊列與 Apache Kafka ”一文。
生產(chǎn)者和消費者相互解耦,異步通信。消息隊列存儲事件,直到它們被成功消費。
大多數(shù)消息隊列中間件產(chǎn)品都提供了內(nèi)置的請求-響應(yīng) API。它的通信是異步的。該實現(xiàn)使用相關(guān) ID。
請求-響應(yīng) API(例如,在 JMS 中)通過從請求中獲取回復(fù)端點來創(chuàng)建一個臨時隊列或主題,該隊列或主題在請求中被引用以供消費者使用。ID 用于將請求與單個請求者分開。這些隊列或主題也僅在請求者與回復(fù)會話處于活動狀態(tài)時可用。
這種帶有臨時隊列或主題的實現(xiàn)在 Kafka 中沒有意義。我實際上已經(jīng)看到企業(yè)試圖這樣做。卡夫卡不是那樣工作的。結(jié)果是 Kafka 集群中有太多的分區(qū)和主題。結(jié)果是可擴展性和性能問題。
異步數(shù)據(jù)流 (Apache Kafka)
數(shù)據(jù)流持續(xù)處理來自數(shù)據(jù)源的攝取事件。此類數(shù)據(jù)應(yīng)使用流處理技術(shù)進行增量處理,而無需訪問所有數(shù)據(jù)。
異步通信范式類似于消息隊列。與消息隊列相反,數(shù)據(jù)流提供了事件的長期存儲和歷史信息的可重放性。結(jié)果是生產(chǎn)者和消費者之間真正的脫鉤。在大多數(shù) Apache Kafka 部署中,具有非常不同的通信范式和延遲能力的多個生產(chǎn)者和消費者發(fā)送和讀取事件。
Apache Kafka 不提供內(nèi)置的請求-響應(yīng) API。 正如有些人認(rèn)為的那樣,這不一定是壞事。數(shù)據(jù)流提供不同的設(shè)計模式。 這就是這篇博文的主要原因!讓我們探索消息系統(tǒng)中請求-響應(yīng)模式的權(quán)衡,并了解更適合數(shù)據(jù)流世界的替代方法。但是這篇文章也探討了如何使用 Kafka 實現(xiàn)異步或同步請求-回復(fù)。
但請記住:不要重復(fù)使用您關(guān)于 HTTP 和 MQ 的“遺留知識”,并嘗試使用 Apache Kafka 重新構(gòu)建相同的模式。話雖如此,Kafka 也可以實現(xiàn)請求-響應(yīng)。更多關(guān)于這方面的內(nèi)容在以下部分中。
請求-回復(fù)與 CQRS 和事件溯源
CQRS (Command Query Responsibility Segregation)規(guī)定,每個方法要么是執(zhí)行操作的命令,要么是向調(diào)用者返回數(shù)據(jù)的查詢,但不能同時是兩者。服務(wù)變得真正相互分離。
Martin Fowler 有一個很好的 CQRS 圖表。
事件溯源是一種架構(gòu)模式,其中實體不使用直接序列化或?qū)ο箨P(guān)系映射來跟蹤其內(nèi)部狀態(tài),而是通過讀取事件并將其提交到事件存儲。
當(dāng)事件溯源與 CQRS 和域驅(qū)動設(shè)計相結(jié)合時,聚合根負(fù)責(zé)驗證和應(yīng)用命令(通常通過從命令處理程序調(diào)用它們的實例方法),然后發(fā)布事件。
使用 CQRS,狀態(tài)會根據(jù)每個相關(guān)的事件消息進行更新。因此,狀態(tài)總是已知的。查詢存儲在物化視圖(例如,Kafka Streams 中的 KTable)中的狀態(tài)是有效的。對于請求響應(yīng),服務(wù)器必須計算或確定每個請求的狀態(tài)。使用 CQRS,它只計算/更新一次,而不管相關(guān)發(fā)生時的狀態(tài)查詢數(shù)量如何。
這些原則完全適合數(shù)據(jù)流世界。Apache Kafka 是一種分布式存儲,可將傳入事件附加到不可變提交日志中。開箱即用的 Kafka 基礎(chǔ)設(shè)施中內(nèi)置了真正的事件解耦和可重放性。大多數(shù)具有領(lǐng)域驅(qū)動設(shè)計的現(xiàn)代微服務(wù)架構(gòu)都是使用 Apache Kafka 構(gòu)建的,而不是 REST 或 MQ。
如果不需要,不要在 Kafka 中使用 Request-Response!
如果您構(gòu)建現(xiàn)代企業(yè)架構(gòu)和新應(yīng)用程序,請應(yīng)用最適合該技術(shù)的自然設(shè)計模式。請記住:數(shù)據(jù)流是一種不同于 Web 服務(wù)和消息隊列的技術(shù)!帶有事件溯源的 CQRS是 Kafka 世界中大多數(shù)用例的最佳模式。
如果你沒有必要,不要在 Kafka 中使用請求-響應(yīng)概念!Kafka 是為流數(shù)據(jù)和真正解耦各種生產(chǎn)者和消費者而構(gòu)建的。
即使對于事務(wù)性工作負(fù)載也是如此。事務(wù)不需要同步通信。Kafka API 支持關(guān)鍵任務(wù)事務(wù)(盡管它本質(zhì)上是異步的和分布式的)。考慮進行銀行付款。它從不是同步的,而是一個復(fù)雜的業(yè)務(wù)流程,在組織內(nèi)部和跨組織中包含許多獨立事件。
Apache Kafka 的同步與異步請求響應(yīng)
在我解釋過請求-響應(yīng)不應(yīng)該是構(gòu)建新的 Kafka 應(yīng)用程序時的第一個想法之后,這并不意味著它是不可能的。有時,它是解決問題的更好、更簡單或更快的方法。因此,讓我們看一下使用 Kafka 實現(xiàn)同步和異步請求-響應(yīng)的示例。
請求-回復(fù)模式可以用 Kafka 來實現(xiàn)。但不一樣。嘗試像在 JMS 消息代理中那樣做(帶有臨時隊列等)最終會殺死 Kafka 集群(因為它的工作方式不同)。盡管如此,使用的概念與 JMS API 中的概念相同,例如相關(guān) ID。
Apache Kafka 的異步請求-響應(yīng)
Spring 項目及其Kafka Spring Boot Kafka 模板庫有一個很好的使用 Kafka 構(gòu)建的異步請求-回復(fù)模式的示例。查看“ org.springframework.kafka.requestreply.ReplyingKafkaTemplate ”。它使用 Kafka API 輕松創(chuàng)建請求/回復(fù)應(yīng)用程序。該示例很有趣,因為它實現(xiàn)了異步請求/回復(fù),如果您使用例如 JMS API,則編寫起來會更復(fù)雜)。
Spring 的優(yōu)點在于 易于使用的模板和方法簽名的可用性。該框架允許使用沒有自定義代碼的設(shè)計模式來實現(xiàn)它們。例如,以下是使用 Kafka 進行請求-回復(fù)的兩種 Java 方法:
爪哇1RequestReplyFuture < K , V , R > sendAndReceive ( ProducerRecord < K , V > 記錄); 2RequestReplyFuture < K , V , R > sendAndReceive ( ProducerRecord < K , V > record , Duration replyTimeout );
結(jié)果是ListenableFuture異步填充的結(jié)果(或異常,超時)。結(jié)果還有一個sendFuture屬性,它是調(diào)用的結(jié)果KafkaTemplate.send()。您可以使用這個未來來確定發(fā)送操作的結(jié)果。
Apache Kafka 的同步請求-響應(yīng)
另一篇優(yōu)秀的 DZone 文章討論了使用 Spring Kafka模板的同步請求/回復(fù)。該示例顯示了一個Kafka 服務(wù),它通過同步請求-響應(yīng)行為計算兩個數(shù)字的總和以返回結(jié)果:
Spring 自動在生產(chǎn)者記錄中設(shè)置相關(guān) ID。@SendTo此關(guān)聯(lián) ID 由消費者端的注釋按原樣返回。
查看DZone 帖子以獲取完整的代碼示例。
Kafka 模板的 Spring 文檔有很多關(guān)于 Kafka 的請求/回復(fù)模式的詳細(xì)信息和代碼示例。使用 Spring,使用 Kafka 實現(xiàn)請求/回復(fù)模式非常簡單。如果您不使用 Spring,您可以學(xué)習(xí)如何在您的框架中使用 Kafka 進行請求-回復(fù)。 這就是開源的美妙之處……
數(shù)據(jù)流和 Rest API 的結(jié)合
上面的示例展示了如何使用 Apache Kafka 實現(xiàn)請求-響應(yīng)模式。盡管如此,它仍然只是次優(yōu)方法,并且通常是流數(shù)據(jù)的反模式。 數(shù)據(jù) 流和請求響應(yīng) REST API 通常結(jié)合使用,以充分利用兩者。我寫了一篇關(guān)于“使用 Apache Kafka 的 HTTP 和 REST API 的用例和架構(gòu)”的專門博客文章。
Apache Kafka 和 API 管理
一種非常常見的方法是使用 Kafka 生態(tài)系統(tǒng)大規(guī)模實時實施應(yīng)用程序,然后在頂部放置一個 API 管理層以將事件作為 API 公開給外部世界(另一個內(nèi)部業(yè)務(wù)域或 B2B 3rd 方應(yīng)用程序) )。
這是連接 SAP 數(shù)據(jù)的示例。SAP 有數(shù)十種與 Kafka 集成的選項,包括 Kafka Connect 連接器、REST/HTTP、專有 API 或 3rd 方中間件。
無論您如何將數(shù)據(jù)獲取到流數(shù)據(jù)中心,在右側(cè),Kafka REST API 用于通過 HTTP 公開事件。API 管理解決方案在Kafka 接口之上處理安全和貨幣化/計費要求:
在博客文章“ Apache Kafka 和 API 管理/API 網(wǎng)關(guān) – 朋友、敵人還是敵人? ”中閱讀有關(guān)此討論的更多信息。它涵蓋了 Apache Kafka 和 API 管理平臺(如 Kong、MuleSoft Anypoint 或 google 的 Apigee)之間的關(guān)系。
使用 Apache Kafka 進行內(nèi)部和外部數(shù)據(jù)共享的流交換
在討論了 API、請求-響應(yīng)通信和 Kafka 之間的關(guān)系之后,讓我們來探討一下市場上的一個重要趨勢:Data Mesh(流行語)和用于實時數(shù)據(jù)共享的流交換(問題解決者)。
數(shù)據(jù)網(wǎng)格是一種新的架構(gòu)范式,近來備受關(guān)注。沒有任何一種技術(shù)可以完美地構(gòu)建數(shù)據(jù)網(wǎng)格。像 Apache Kafka 這樣的開放且可擴展的去中心化實時平臺通常是 Data Mesh 基礎(chǔ)架構(gòu)的核心,并輔以許多其他數(shù)據(jù)平臺來解決業(yè)務(wù)問題。
流原生數(shù)據(jù)共享而不是在中間使用請求-響應(yīng)和 REST API 是許多用例的自然演變:
在“使用 Kafka 和動態(tài)數(shù)據(jù)網(wǎng)格進行流式數(shù)據(jù)交換”一文中了解更多信息。
一起使用數(shù)據(jù)流和請求響應(yīng)!
大多數(shù)架構(gòu)需要點對點通信(例如,服務(wù)器和移動應(yīng)用程序之間)的請求-響應(yīng)和連續(xù)數(shù)據(jù)處理的數(shù)據(jù)流。
可以使用 Apache Kafka 實現(xiàn)同步和異步請求-響應(yīng)通信。但是,CQRS 和事件溯源在大多數(shù)情況下是更好、更自然的數(shù)據(jù)流方法。了解不同的選項及其權(quán)衡,并為工作使用正確的工具(在這種情況下,是正確的設(shè)計模式)。