Rocket MQ消息中間件
Rocket MQ 背景
至 2010 年,隨著阿里業務的快速發展,急需一款支持順序消息,擁有海量消息堆積能力的消息 Meta Q 1.0 在 2011 年誕生。
到 2012 年, Meta Q 已經發展到了 Meta Q 3.0 ,并抽象出了通用的消息引擎 Rocket MQ。隨后,將 Rocket MQ 進行了開源。
到 2016 年, Meta Q 在當年雙十一承載了萬億級消息的流轉,跨越了一個新的里程碑,同時 Rocket MQ 進入 Apache 孵化。
圖片
Rocket MQ 是什么
- 是一個隊列模型的消息中間件,具有高性能、高可靠、高實時、分布式特點
- Producer、Consumer、隊列都可以分布式
- Producer 向一些隊列輪流發送消息,隊列集合稱為 Topic,Consumer 如果做廣播消費,則一個 Consumer 實例消費這個 Topic 對應的所有隊列,如果做集群消費,則多個 Consumer 實例平均消費這個 Topic 對應的隊列集合
- 能夠保證嚴格的消息順序
- 提供豐富的消息拉取模式
- 高效的訂閱者水平擴展能力
- 實時的消息訂閱機制
- 億級消息堆積能力
- 較少的依賴
Rocket MQ 術語
Topic:標識一類消息的邏輯名字,消息的邏輯管理單位。無論消息生產還是消費,都需要指定 Topic。
Tag:Rocket MQ 支持給在發送的時候給 Topic 打 Tag,同一個 Topic 的消息雖然邏輯管理是一樣的。但是消費 Topic1 的時候,如果你訂閱的時候指定的是 Tag A,那么 Tag B 的消息將不會投遞。
Message Queue:簡稱 Q。一個 Topic 將有若干個 Q。若 Topic 同時創建在不通的 Broker,則不同的 Broker 上都有若干 Q,消息將物理地存儲落在不同 Broker 結點上,具有水平擴展的能力。
Offset:可以認為一條邏輯的 Message Queue 是無限長的數組。一條消息進來下標就會漲 1,而這個數組的下標就是 Offset。
Max Offset:這個 Offset 實際上是最新消息的 Offset + 1,即:下一條消息的 Offset。
Min Offset:標識現存在的最小 Offset。而由于消息存儲一段時間后,消費會被物理地從磁盤刪除,Message Queue 的 Min Offset 也就對應增長。這意味著比 Min Offset 要小的那些消息已經不在 Broker 上了,無法被消費。
Consumer Offset:表示的是下次拉取的 Offset 位置。
Rocket MQ 模塊
Name Server
存儲當前集群所有 Brokers 信息、Topic 跟 Broker 的對應關系。
Broker
集群最核心模塊,主要負責 Topic 消息存儲、消費者的消費位點管理(消費進度)。只有 Master 才能進行寫入操作,Slave 從 Master 中同步數據。
Producer
生產者。發送消息的客戶端角色。發送消息的時候需要指定 Topic。Producer 完全無狀態,可集群部署。
Consumer
消費者,通常有兩種實現,分別為 Push Consumer 和 Pull Consumer,通常我們采用 Push 的方式為主, Pull 的為輔的來進行消息的消費。
集群部署架構
圖片
集群工作流程
- 啟動 Name Sever, Name Sever 起來后監聽端口,等待 Broker、 Producer 、Consumer 連上來,相當于一個路由控制中心。
- Broker 啟動,跟所有的 Name Sever 保持長連接,定時發送心跳包。心跳包中包含當前 Broker 信息(IP + 端口等)以及存儲所有 Topic 信息。注冊成功后, Name Sever 集群中就有 Topic 跟 Broker 的映射關系。
- 收發消息前,先創建 Topic,創建 Topic 時需要指定該 Topic 要存儲在哪些Broker 上。也可以在發送消息時自動創建 Topic。
- Producer 發送消息,啟動時先跟 Name Sever 集群中的其中一臺建立長連接,并從 Name Sever 中獲取當前發送的 Topic 存在哪些 Broker 上,然后跟對應的Broker 建長連接,直接向 Broker 發消息。
- Consumer 跟 Producer 類似。跟其中一臺 Name Sever 建立長連接,獲取當前訂閱 Topic 存在哪些 Broker,然后直接跟 Broker 建立連接通道,開始消費消息。
模塊功能特性
Name Sever
- Name Sever 用于存儲 Topic、Broker 關系信息,功能簡單,穩定性高。多個 Name Sever 之間相互沒有通信,單臺 Name Sever 宕機不影響其他 Name Sever 與集群;即使整個 Name Sever 集群宕機,已經正常工作的 Producer,Consumer,Broker 仍然能正常工作,但新起的 Producer, Consumer,Broker 就無法工作。
- Name Sever 壓力不會太大,平時主要開銷是在維持心跳和提供 Topic-Broker 的關系數據。但有一點需要注意,Broker 向 Name Sever 發心跳時,會帶上當前自己所負責的所有 Broker 信息,如果 Topic 個數太多(萬級別),會導致一次心跳中,就 Topic 的數據就 幾十M,網絡情況差的話,網絡傳輸失敗,心跳失敗,導致 Name Sever 誤認為 Broker 心跳失敗。
Broker
高并發讀寫服務
- 消息順序寫:所有 Topic 數據同時只會寫一個文件,一個文件滿 1G,再寫新文件,真正的順序寫盤,使得發消息 TPS 大幅提高。
- 消息隨機讀:Rocket MQ 盡可能讓讀命中系統 Page Cache,因為操作系統訪問 Page Cache 時,即使只訪問 1K 的消息,系統也會提前預讀出更多的數據,在下次讀時就可能命中 Page Cache ,減少 IO 操作。
負載均衡與動態伸縮
- Topic 維度:假如一個 Topic 的消息量特別大,但集群水位壓力還是很低,就可以擴大該 Topic 的隊列數,Topic 的隊列數跟發送、消費速度成正比。
- Broker 維度:如果集群水位很高了,需要擴容,直接加機器部署 Broker 就可以。Broker 起來后向 Name Sever 注冊,Producer、Consumer 通過 Name Sever 發現新 Broker,立即跟該 Broker 直連,收發消息。
- 負載均衡:Broker 上存 Topic 信息,Topic 由多個隊列組成,隊列會平均分散在多個 Broker 上,而 Producer 的發送機制保證消息盡量平均分布到所有隊列中,最終效果就是所有消息都平均落在每個 Broker 上。
- 動態伸縮能力(非順序消息):Broker 的伸縮性體現在兩個維度:Topic, Broker。
高可用&高可靠
- 高可用:集群部署時一般都為主備,備機實時從主機同步消息,如果其中一個主機宕機,備機提供消費服務,但不提供寫服務。
- 高可靠:所有發往 Broker 的消息,有同步刷盤和異步刷盤機制;同步刷盤時,消息寫入物理文件才會返回成功,異步刷盤時,只有機器宕機,才會產生消息丟失,Broker 掛掉可能會發生,但是機器宕機崩潰是很少發生的,除非突然斷電。
Broker 與 Name Sever 的心跳機制
單個 Broker 跟所有 Name Sever 保持心跳請求,心跳間隔為30秒,心跳請求中包括當前 Broker 所有的 Topic 信息。Name Sever 會反查 Broker 的心跳信息,如果某個 Broker 在2分鐘之內都沒有心跳,則認為該 Broker 下線,調整 Topic 跟 Broker 的對應關系。但此時 Name Sever 不會主動通知 Producer、Consumer 有 Broker 宕機。
Broker 刷盤策略
刷盤策略:Rocket MQ 的所有消息都是持久化的,先寫入系統 Page Cache,然后刷盤,可以保證內存不磁盤都有一份數據,訪問時,直接從內存讀取。
異步刷盤: 舉例:現有 SAS 15000 轉磁盤測試順序寫文件,速度可以達到 300M 每秒左右,而線上的網卡一般都為千兆網卡,速度最快可達 128M 每秒, 寫磁盤速度明顯快于數據網絡入口速度,那么刷盤的迕度肯定可以跟上消息的寫入速度。
同步刷盤:和異步的唯一區別是異步寫完 Page Cache 直接返回,而同步是等待刷盤完成之后再返回。
圖片
Producer
- Producer 啟動時,也需要指定 Name Sever 的地址,從 Name Sever 集群中選一臺建立長連接。如果該 Name Sever 宕機,會自動連其他 Name Sever 。直到有可用的 Name Sever 為止。
- 心跳檢測和 Broker 類似。
- Producer 端,每個實例在發消息的時候,默認會輪詢所有的 Message Queue 發送,以達到讓消息平均落在不同的 Queue 上。而由于 Queue 可以散落在不同的 Broker,所以消息就發送到不同的 Broker 下。當然了,可以發送消息到指定的隊列中。比如根據店家的編號 Hash 到不同的隊列中,形成有序的消息。如果想自己實現發送的策略,可以實現 MessageQueueSelector 這個接口。
圖片
Consumer
- 消費者啟動時需要指定 Name Sever 地址,與其中一個 Name Sever 建立長連接。消費者每隔 30 秒從 Name Sever 獲取所有 Topic 的最新隊列情況,這意味著某個 Broker 如果宕機,客戶端最多要 30 秒才能感知。連接建立后,從 Name Sever 中獲取當前消費 Topic 所涉及的 Broker,直連 Broker。
- 和 Name Sever 的心跳檢測和 Broker 類似。
- 消費者消費模式
- 集群消費:一個 Consumer Group 中的各個 Consumer 實例分攤去消費消息,即一條消息只會投遞到一個 Consumer Group 下面的一個實例。
- 廣播消費:消息將對一個 Consumer Group 下的各個 Consumer 實例都投遞一遍。即即使這些 Consumer 屬于同一個 Consumer Group,消息也會被 Consumer Group 中的每個 Consumer 都消費一次。
- 負載均衡:消費者端的負載均衡,就是集群消費模式下,同一個 ID 的所有消費者實例平均消費該 Topic 的所有隊列。
- 拉取流程:Consumer 端每隔一段時間主動向 Broker 發送拉消息請求,Broker 在收到 Pull 請求后,如果有消息就立即返回數據,Consumer 端收到返回的消息后,再回調消費者設置的 Listener 方法。如果 Broker 在收到 Pull 請求時,消息隊列里沒有數據,Broker 端會阻塞請求直到有數據傳遞或超時才返回。
圖片
持久化 Commit Log
雖然每個 Topic 下面有很多 Message Queue,但是 Message Queue 本身并不存儲消息。真正的消息存儲會寫在 Commit Log 的文件,Message Queue 只是存儲 Commit Log 中對應的位置信息,方便通過 Message Queue 找到對應存儲在 Commit Log 的消息。不同的 Topic,Message Queue 都是寫到相同的 Commit Log 文件,也就是說 Commit Log 完全的順序寫。
圖片
什么時候清理物理消息文件
消息存儲在 Commit Log 之后,的確是會被清理的,但是這個清理只會在以下任一條件成立才會批量刪除消息文件(Commit Log):
- 消息文件過期(默認72小時),且到達清理時點(默認是凌晨4點),刪除過期文件。
- 消息文件過期(默認72小時),且磁盤空間達到了水位線(默認75%),刪除過期文件。
- 磁盤已經達到必須釋放的上限(85%水位線)的時候,則開始批量清理文件(無論是否過期),直到空間充足。
注意:若磁盤空間達到危險水位線(默認90%),出于保護自身的目的,Broker 會拒絕寫入服務。
參考文獻
- Rocket MQ 用戶指南 v3.2.4
- 阿里云社區
https://yq.aliyun.com/articles/69647?spm=5176.100240.searchblog.7.ZgGuDF
https://yq.aliyun.com/articles/66101?spm=5176.100240.searchblog.101.s7dvlU
https://yq.aliyun.com/articles/66110?spm=5176.100239.blogcont66101.23.kpzm2R