圖解 RocketMQ 運行原理
當我們談到可靠的、高吞吐量的分佈式消息隊列,RocketMQ 必將成爲我們不得不提及的一個話題。作爲一個開放、高度可擴展的消息中間件,RocketMQ 已經在各種場景下得到廣泛的應用,如電商、物流、支付等領域。在該領域中,RocketMQ 以其出衆的運行性能和可靠性優勢,贏得了越來越多企業和開發者的信賴。
本文將深入剖析 RocketMQ 的運行原理,從 Producer、Broker、Consumer 三個模塊的角度,通過圖文並茂的方式,從 Producer 如何寫入消息,Broker 如何同步消息,Consumer 如何消費消息, 分別探討它們的運行結構和原理,並進一步探討該中間件的不同應用場景和最佳實踐,希望能對您有所幫助!
一、RocketMQ 運行原理
RocketMQ 運行原理
上面是 RocketMQ 運行的一個大致流程圖。
在對 Apache RocketMQ 進行深入探索的過程中,我們首先需要理解其核心組件的作用:
NameServer:充當註冊中心的角色,主要任務是管理 Broker 節點.
Broker:它是 RocketMQ 系統的核心部分,主要負責消息的存儲。
Producer:這是消息的生成者,它創建消息並將其寫入 Broker。
Consumer:作爲消息的接收者,負責從 Broker 讀取消息並進行處理。
下面,讓我們一起深入瞭解 RocketMQ 的運行流程:
-
首先,Broker 在啓動後會向根據其配置向 NameServer 註冊。
-
NameServer 作爲註冊中心,管理着我們的 Broker 集羣信息以及 Topic 路由信息。例如,一個特定的 Topic 具有哪些 Broker 主機以及隊列信息。
-
接着,由特定的業務系統中的生產者(Producer)生成消息,併發送到 Broker 的主節點。
-
在 Broker 節點中,這些消息將被保存到本地磁盤的 CommitLog 中,以確保消息不會丟失。
-
接下來,主節點 Broker 將這些消息同步到從節點 Broker,這樣可以實現負載均衡並增強系統的魯棒性。
-
最後,業務系統中的消費者(Consumer)會從 Broker 中取出消息並進行處理,這就完成了數據的完整生命週期。
我們的 Producer 寫入消息前需要先選擇 Broker,那 Producer 是如何選擇 Broker 的呢?
Producer 選擇 Broker
上面提到,NameServer 在 RocketMQ 架構中起到了註冊中心的作用,它負責管理所有的 Broker 節點。每當 Broker 啓動後,它就會自動的註冊到 NameServer 中,並且會每隔 30 秒向 NameServer 發送一次心跳,以證明它依然在運行。NameServer 則會每隔 10 秒檢查一次各個 Broker 節點是否還在線,如果有 Broker 在 120 秒內未發送心跳,那麼 NameServer 就會判斷該 Broker 已經宕機,進而將其從註冊列表中移除。
在業務系統中,Producer 在發送消息之前,會先從 NameServer 中拉取需要的 topic 路由信息,這些信息將包含目標 topic 各個 queue 的詳細信息,以及各 queue 分別存儲在哪個 Broker 節點上。Producer 會將這些信息緩存到本地,並依此信息,通過一種負載均衡算法,選擇從哪個 queue 中讀取數據,以及找到該 queue 對應的 Broker 節點。
那麼,如果某個 Broker 在 Producer 準備寫入數據的時候突然宕機了,又該如何處理呢?
RocketMQ 設計了一套故障探測與處理機制。如果某個 Broker 宕機了,那麼 Producer 進行寫入操作時將會失敗,此時,它會發起重試操作,並從可用的 Broker 列表中重新選擇一個進行寫入。並且,爲避免持續向故障節點寫入數據,Producer 會採取一種稱爲 "故障退避" 的策略,即在一段時間內停止向該 Broker 發送數據。值得注意的是,Broker 的故障並不會立即被 Producer 和 NameServer 感知,這樣做是爲了降低 NameServer 處理邏輯的複雜性。當 Broker 宕機後,由於本地的 topic 路由緩存並未更新,Producer 仍可能嘗試向故障的 Broker 發送數據,然後備受失敗並重試。只有當 NameServer 在檢查心跳時發現該 Broker 已宕機,並從註冊列表中移除後,Producer 在刷新本地緩存時,纔會真正地感知到該 Broker 的宕機。
當我們的 Producer 基於負載均衡選擇了 Broker 節點,它的消息是如何寫入的呢?
Producer 寫入消息 - 默認情況
在深入理解 RocketMQ 的存儲機制時,我們需要知道,Producer 在寫入消息時,默認會優先寫到操作系統管理的 pageCache,這個過程是異步的,只要消息被寫入 pageCache,寫入操作就被認爲是成功的。這種異步的處理方式極大地提高了 RocketMQ 的寫入效率。
當消息被寫入 pageCache 後,將有一個後臺線程異步地將這些消息從 pageCache 刷入到磁盤文件 CommitLog 中,CommitLog 是消息的實際存儲位置。同時,還會有一個專門的線程負責將 CommitLog 中的消息位置(物理偏移量)寫入到 ConsumeQueue 中。
那麼客戶端如何讀取存儲在 CommitLog 中的消息呢?
當 Consumer 端的消費者需要讀取消息時,它會先到 ConsumeQueue,然後根據在 ConsumeQueue 中存儲的 offset 信息找到 CommitLog 中的實際數據進行讀取。
這樣的存儲方法是否可以支持高併發模式的寫入呢?
當系統面臨大量同時寫入和讀取的請求時,可能會遇到一種情況,即大量的讀取請求通過 ConsumeQueue 去找 CommitLog 中的數據,但是此時數據可能還在 pageCache 中並未完成異步寫入。這時,系統會通過 CommitLog 和 PageCache 的映射,找到 pageCache 中的消息進行讀取。也就是說,大量的讀取和寫入請求都對 pageCache 進行操作。但是 當併發量過高時,可能會出現 "Broker busy" 的異常, 這是因爲在極高的併發場景下,持續大量的讀寫操作可能會對系統性能造成影響。
簡而言之,RocketMQ 的存儲機制旨在爲高併發高效的讀寫提供支持,但是在一些極端情況下,仍然需要額外的優化措施以提高穩定性和性能。
當併發量非常高時,出現 Broker busy 異常了,如何解決?
Producer 寫入消息 - 開啓 transientStorePool 機制
RocketMQ 在面對高併發場景時,爲了改善 "Broker busy" 異常和提高吞吐量,可以 啓用 transientStorePool 機制。 這種機制的實現方式是,Broker 在寫入消息時,將消息直接寫入由 JVM 管理的 offheap 堆外內存,這樣的設計能有效提升併發性能。
那麼,爲什麼啓用 transientStorePool 可以提高併發處理能力呢?
當開啓該機制後,消息首先寫入 JVM 的 offheap 內存,然後異步刷新到 pageCache,最終由 pageCache 異步刷新到 CommitLog。大量的寫請求將向 JVM 的 heap 內存進行,而大量的讀請求仍然從 pageCache 進行,這種讀寫分離的機制極大地提高了 RocketMQ 的併發性能。
但是爲什麼 transientStorePool 機制不作爲默認機制呢?
雖然 transientStorePool 能顯著提升併發性能,但其也存在風險。當消息寫入到 JVM 管理的 offheap 堆外內存後,如果 JVM 進程重啓或者宕機,那些尚未被及時落盤的消息就會丟失。但如果採用默認的寫入方法,即先寫入操作系統管理的 pageCache,那麼在 JVM 進程重啓後,那些保存在 pageCache 中的信息不會丟失,只有當整個服務器宕機重啓時,pageCache 中的消息纔有可能丟失。因此,數據最安全的處理方式是,將其直接寫入到 CommitLog。
總結:開啓 transientStorePool 機制可以極大地提高 RocketMQ 的併發處理能力,然而這可能會帶來數據的丟失。因此,它更適合那些併發處理能力要求高、且可以接受部分數據丟失的場景。
如果我們想要寫入數據不丟失,應該怎麼處理?
Producer 寫入消息 - 同步寫入 CommitLog 機制
在設計與金融場景以及其他要求數據不能丟失的環境中,我們會採用同步方式將數據寫入 CommitLog。成功執行寫入操作後才返回,確保了數據的完整性和安全性。只有在 broker 的物理存儲設備出現故障的情況下,纔有可能導致數據丟失。爲了提供進一步提高數據的安全性,也可以通過多臺服務器進行數據備份。
但值得注意的是, 儘管實現了對數據的安全性提升, 使用同步寫入 CommitLog 方式會降低系統的性能到幾個數量級。
我們已經瞭解了生產者(Producer)的消息如何寫入到消息隊列中間件(Broker)以及各種寫入方式。爲了實現高可用性,多臺 Broker 通常會一同存儲寫入的消息,各自作爲主節點和從節點。那麼,這些節點之間是如何實現數據同步的呢?
二、主從同步
主從同步過程
在業務系統中,生產者(Producer)將數據寫入消息隊列中間件(Broker),再由 Broker 的主節點同步到從節點。Broker 的主從複製主要採用 push 和 pull 兩種模式。
在 push 模式中,生產者將數據寫入主節點 Broker 後,主節點主動推送數據到從節點,實現數據的同步。
相反,pull 模式則是在生產者將數據寫入主節點 Broker 後,主節點等待從節點主動發起請求並拉取數據以完成同步操作。
通過這兩種模式,數據可以在 Broker 的主節點和從節點之間有效地進行同步,保證了數據的一致性和完整性。
主從同步過程 - Pull 方式
接下來,我們將詳細介紹 pull 模式的數據拉取過程:
首先,Broker 的主節點在啓動後會監聽從節點的連接請求,並在接到請求後建立連接。一旦連接建立,主節點會初始化 HAConnection 組件。這個組件的主要職責是監聽從節點的連接;而對於主節點,每當從節點連接成功,它都會爲這個從節點創建專屬的 HAConnection。
在完成連接後,從節點會創建兩個線程,也就是兩個 HAClient:一個負責處理主從同步的請求,一個負責處理主從同步的響應。
HAClient 創建成功後,每經過一段時間,就會發出 pull 請求,請求同步主節點的數據。需要同步的數據的範圍是指定在 CommitLog 中以特定物理偏移量(Offset)開始的所有消息。
收到請求後,主節點會將 offset 指定位置之後的所有數據發回給從節點。從節點在接收到這些數據後,會由 HAClient 的響應線程將這些數據寫入本地的 CommitLog。
然而,當消息被寫入到 pageCache 且當前服務器突然宕機,該部分數據還未得以同步到從節點。由於 pageCache 是由操作系統管理,這就導致了在切換到從節點時,這部分數據將被丟失。
那麼,我們應該如何預防數據在同步過程中丟失呢?
主從同步過程 - 數據 0 丟失
在涉及金融等對數據安全性要求極高的場景中,我們通常採用同步寫入 CommitLog 的機制來防止數據丟失。操作步驟如下:
首先,寫操作會在主節點上進行一次磁盤 IO,隨後等待從節點發送 pull 請求。然後,從節點會查詢本地 CommitLog 以獲取最大的物理偏移量 offset,這次查詢會進行一次磁盤 IO,之後把查詢結果通過網絡傳輸向主節點發出。這個過程中包含一次網絡 IO。
一旦主節點接收到此請求,它會通過查詢和對比本地磁盤的 offset,需進行一次磁盤 IO。然後,主節點將最新的數據發送給從節點,包含一次網絡 IO。消息到達從節點之後,進行一次磁盤 IO 寫入操作。
由此可見,爲了保證數據的完整性,這個過程中涉及了多次網絡 IO 和磁盤 IO 操作。然而這樣的過程會降低系統性能。原本只需幾十毫秒,幾百毫秒即可完成的寫入操作,現在可能需要幾百毫秒或者可能延長到幾秒。
總的來說,採用同步寫入 + Pull 方式可以防止數據丟失,但是頻繁的 IO 操作會對性能造成影響。
主從同步過程 - push 方式
我們剛剛討論了利用 pull 模式進行主從同步的過程,以及如何確保 100% 不丟失任何數據。接下來,我們將探討 push 模式的主從同步過程。
在一個理想的情景中,需利用 push 模式同步數據時,主節點 Broker 將消息寫入後,默認 的操作是成功寫入 pageCache 就返回。這之後,還會有一個線程異步地進行持久化刷盤。與之不同,這個過程並不需等待從節點發起 pull 請求,而是直接 push 數據至從節點。顯然,採用 push 方式減小了時間開銷。 一旦從節點 Broker 接收到最新數據,它也只需成功寫入 pageCache 就可以返回了。然後後臺的線程會異步地進行持久化操作。
明顯地,採用 push 方式的性能非常高,因爲只需在內存中完成寫入操作就可以返回結果。
總的來說,採取異步寫入 + Push 的方式性能高,但是如果當前的 Broker 所在的服務器突然宕機,數據可能丟失。所以,如果旨在保證數據安全性和性能,可以綜合採用同步寫入 + Push 的方式。
至此,我們已經介紹了生產者如何寫入消息至 Broker,以及 Broker 如何同步數據。接下來,讓我們探討在 Broker 中的數據如何被消費者消費。
三、Consumer 消費
Consumer 消費 - 如何選擇 queue
在開始消費 Broker 中的消息之前,我們需要爲消費者 (Consumer) 選擇一個適合的隊列(queue)。RocketMQ 採用了一種特殊的流程來進行隊列選擇。
消費者實際上是通過消費者組(ConsumerGroup)進行消費的。每個消費者在啓動後,會將自身的信息註冊至每個 Broker。我們提到過,Broker 可以獲取到命名服務器(NameServer)中的主題路由信息,這些信息中包含了隊列的相關信息。如果每個消費者都向每個 Broker 進行註冊,那麼每個 Broker 便可以獲取到消費者組中每個消費者的信息。
消費者內部有一個稱爲 BalanceService 的組件,該組件每隔 20 秒就會拉取 Broker 中的主題路由信息以及消費者組的信息。 BalanceService 組件接着利用特定的分配算法爲每個隊列分配一個消費者。
常用的分配算法有平均分配、輪詢分配以及基於機房的分配等。具體使用哪種算法,取決於實際的系統需求和設計。
因此,RocketMQ 通過這種方式爲每個消費者選擇合適的消息隊列,從而有效地負載均衡消費任務,提高系統的整體性能。
Consumer 消費 - 拉取消息
一旦消費者(Consumer)完成了消息隊列(queue)的分配,它便會啓動一個線程開始拉取消息。
正如我們剛剛所提到的,默認情況下,生產者(Producer)會先將消息寫入 pageCache,然後系統在後臺會將 pageCache 中的數據異步刷盤到 CommitLog,最後一系列監聽會將消息寫入 ConsumeQueue。
消費者在拉取 ConsumeQueue 中的消息後,會在內存中創建一個 ProcessQueue,並將拉取到的消息寫入其中。ProcessQueue 實際上是對 ConsumeQueue 中的消息進行了一一映射,這樣就完成了消息的消費準備。
這個過程中,PageCache 的使用、以及消息從 ConsumeQueue 到 ProcessQueue 的一一映射,都是 RocketMQ 在設計上爲了確保消息處理效率的考慮。這樣執行,不僅保證了消息處理的高速性,也確保了消息消費的準確性。
Consumer 消費 - 消息處理
在拉取線程將消息成功寫入 ProcessQueue 之後,消費者(Consumer)會啓動一個線程池併發地處理這些消息。消費者在處理過程中會調用業務方實現的監聽器函數,可以包含各種業務邏輯,如執行對 MySQL 的增刪改查操作,執行對 Redis 的鍵值對操作,以及發起對其他服務的遠程過程調用。
在業務邏輯處理完消息後,情況如何會有兩種可能的回饋。如果消息處理成功,業務方會返回一個 SUCCESS 的狀態。若消息處理失敗,業務方則會返回 RECONSUME_LATER 的狀態,意味着這條消息會被重新消費。
這樣,RocketMQ 的消費者既可以併發地高效處理大量的消息,也可以根據業務處理結果進行後續的優化,進一步提升了系統的處理能力和穩定性。
Consumer 消費 - 消費成功 SUCCESS
在消息被成功消費後,消費者(Consumer)將返回一個 "Success" 狀態。接着,消費者會刪除 ProcessQueue 中的對應消息,並保存消費進度。消費進度標誌着當前隊列(queue)已經消費到哪個位置,這是非常關鍵的一個點。
消費進度的保留有重要的作用。如果 Broker 保存了消費進度,當消費者的節點數量發生變化(如增加或減少)時,Consumer 內的 BalanceService 組件可以根據獲取的主題中的隊列信息和消費者組裏的消費者信息,重新進行隊列分配。因爲之前的消費進度已經持久化保存在 Broker 中,新分配的消費者只需要從未消費的偏移量(offset)繼續消費即可。
處理完消息後,消費者會在內存中保存消費進度,並且由一個後臺線程異步地將消費進度發送給 Broker 內存。在 Broker 內存中,也會進行持久化處理,將消費進度保存在磁盤文件,從而實現消費進度的持久化。
我們剛講述了處理消費成功的消息的過程,接下來我們來講解一下當消息消費失敗後的處理流程。
Consumer 消費 - 消費失敗 RECONSUME_LATER
在處理業務消息後,如果需要重新消費消息(標記爲 RECONSUME_LATER),通過 ACK(Acknowledgement)機制,這個信息將被傳達給 RocketMQ 的 Broker。Broker 在接收到這個信號後,首先會對這條失敗的消息進行主題(topic)的重寫,然後將其存儲到 CommitLog 以及 ConsumeQueue。這是 RocketMQ 的基本消息存儲機制,通過將消息寫入這兩個文件,確保了消息的安全存儲。
之後,Broker 同時會開啓一個定時檢查,建立一個等待重新消費的時間窗口。這種機制允許 Broker 在一定時間內重新發送消息,避免了消息的立即重試可能導致的資源過載問題。在設定的重試時間到達後,Broker 會將之前改寫的主題恢復原狀,然後進行消息的重新消費。
通俗來講,這個過程就像是投遞人將一封信投遞失敗後,把信件暫時存儲起來。同時,他會設定一個提醒,等到合適的時間再次嘗試投遞這封信。在此過程中,RocketMQ 的 Broker 就扮演着這個盡職的投遞人的角色,確保每一條消息最終都能準確無誤地送達。
通過這樣的機制,RocketMQ 能夠有效地處理消費失敗的消息,並根據需要進行重新消費,從而提升了系統的容錯性和消息處理的可靠性。
Consumer 消費 - 消費詳細流程
綜述一下整個 RocketMQ Consumer 消費的流程:
在消費啓動之前,Consumer 需要選擇自己需要消費的 queue。一旦啓動,Consumer 會向 Broker 進行註冊。
Consumer 會藉助 BalanceService 組件從 Broker 中拉取 topic 路由信息以及 Consumer 組的信息。在獲取這些信息後,基於預設的分配算法,對當前 Consumer 進行 queue 的分配。
分配完成後,Consumer 會觸動拉取線程,從 Broker 中拉取消息並將其寫入 ProcessQueue。接着,Consumer 會啓動線程併發處理 ProcessQueue 中的消息。
在業務處理過程中,會實現對監聽函數的回調。
如果業務處理成功,系統會返回 SUCCESS。此時,會先維護並清理 ProcessQueue 中的消息,接着在內存中記錄消費進度。最後,此消費進度數據會被同步回 Broker 實現持久化。
倘若業務處理失敗,系統會返回 RECONSUME_LATER。Consumer 會通知 Broker 進行失敗消息的 topic 改寫。Broker 接着會實施定時檢查機制,在適當的時間點再將改寫後的消息還原,重新寫入待消費消息流。
在說明這些之後,我將簡述 RocketMQ 的 “過半寫入” 機制。在 RocketMQ 的消息寫入 Broker 過程中,消息寫入不是需要在所有結點都寫入纔算成功,而是當消息成功寫入到節點數目過半時,即可認爲是寫入成功。這種機制保證了 RocketMQ 的高可用性和分佈式一致性。
四、基於 raft 協議的過半寫入機制
在這裏我們考慮有三個 Broker 節點的情況,即 Broker01 作爲主節點,以及 Broker02 和 Broker03 作爲從節點。
當 Producer 將消息寫入 Broker01 主節點時,Broker01 只需將消息順利寫入 pageCache(頁高速緩存)即視爲寫入成功,該節點會記入寫入操作並在後臺進行異步持久化。
緊接着,基於 RAFT 協議,Broker01 節點會將消息同步至從節點 Broker02 和 Broker03,並將消息寫入他們的 pageCache。只要在 Broker02 和 Broker03 中有一個節點成功寫入,整個寫入操作即視爲成功。這是因爲在這三節點系統中,只需要有過半節點(即 2 個節點)寫入成功,整個系統即認定消息已成功寫入。
假設此時 Broker01 節點發生故障,系統會通過 Leader 選舉機制,讓 Broker02 或 Broker03 中的一個節點升級爲主節點,保證消息能繼續被寫入。此時只有兩個節點,只有當寫入消息的操作在這兩個節點中全部成功完成,才能被視爲成功。這同樣體現了 "過半寫入" 的原則。
我們之前提到,如果 Broker01 出現故障,Broker02 或者 Broker03 會基於 Leader 選舉機制進行選舉。在 RocketMQ 系統中,由於種種原因(比如硬件故障、網絡故障或操作系統崩潰等),Broker 節點有可能變得不可用。面對這種情況,基於 Raft 協議的 Leader 選舉機制將發揮關鍵角色,選出新的主節點,以確保消息傳輸的穩定性和可靠性。
五、基於 raft 協議的 Leader 選舉機制
這裏我們要討論 RocketMQ 中的領導選舉機制,類似於設計模式中的狀態機模式,爲了便於理解,我們將其分爲三種角色:Follower(跟隨者)、Candidate(候選人)、以及 Leader(領導)。
首先我們關注 Follower 角色,它是節點初始狀態,即 Broker 節點在一開始就處於 Follower 狀態。在此狀態中,節點設有一個隨機倒計時,如果 Follower 收到了 Leader 的生命信號(心跳),這個倒計時將被重置,這意味着只要有心跳信息,Follower 狀態將一直保持。然而,如果這個隨機倒計時結束了,Follower 角色會升級爲 Candidate 角色。
作爲 Candidate,其主動行爲是發起投票尋求成爲 Leader,對於接收的投票,如果投票數大於或等於整個集羣節點數的一半,它將升級爲 Leader 狀態,如果小於這個數量,它會降級爲 Follower 狀態。
Leader 角色則是系統的核心,它的主動行爲是發送心跳信號保持其他 Follower 節點的跟隨狀態,避免他們試圖搶奪領導地位。
舉例來說,我們這裏有三個 Broker 節點,即 Broker01,Broker02,和 Broker03。他們剛啓動,目的是要選舉出一個作爲 Leader。假設 Broker01 節點首先完成了隨機倒計時,它將首先變成 Candidate,並開始發起投票。如果其餘兩個節點,即 Broker02 和 Broker03 都把票投給了 Broker01,那麼 Broker01 將會升級爲 Leader,併發送心跳給 Broker02 和 Broker03。一旦 Broker02 和 Broker03 接收到心跳,他們會重置自己的倒計時和狀態,一直保持在 Follower 狀態。這就是一種狀態轉換的情況。
接下來,我們來討論另一種領導選舉的情形。假設在三個 Broker 節點剛啓動時,Broker01 和 Broker02 都完成了隨機倒計時並升級爲 Candidate 狀態,並同時發起投票。
在這次投票中,Broker01 投自己一票,Broker02 也投自己一票,而 Broker03 則選擇投給 Broker01。那麼由於 Broker01 的票數達到了過半(二分之一以上),它將成功升級爲 Leader。在升級後,Broker01 將開始發出心跳信號。
另一方面,Broker02 由於未能得到過半的選票(只有自己的一票),將不得不降級爲 Follower,然後重新開始新一輪的隨機倒計時,等待下一次的領導選舉機會。
在這種情況下,雖然 Broker01 和 Broker02 同時成爲 Candidate 併發起投票,但由於得票數量的原因,Broker01 最終升級爲 Leader,而 Broker02 則只能繼續留在 Follower 狀態。這也是 RocketMQ 中領導選舉機制的一種典型應用。
六、總結
本文詳細論述了在 RocketMQ 中,消息是如何 從 Producer 發送給 Broker 的過程, 以及 Producer 具備的不同寫入方式。同時,我們也解讀了 基於 Raft 協議的過半寫入機制。
在數據存儲方面,我們深入探討了消息在 Broker 中的存儲與同步過程。接着,我們又闡釋了 Consumer 如何進行消息消費的具體步驟。
在最後部分,我們討論了當網絡故障發生時,如何通過 w基於 Raft 協議的 Leader 選舉機制選出新的主節點。 這個機制確保了 RocketMQ 系統在面臨故障時,總能保持可用狀態,滿足用戶的服務需求。
希望這些內容能深入淺出地幫助大家理解 RocketMQ 的運作機制和其背後的設計理念,從而使得大家在使用 RocketMQ 產品時,能更加得心應手,並充分發揮出 RocketMQ 的優秀性能。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/oPD9p_nDDLh_zYOwxu-Sdw