李志濤

導航

統計

Apache RocketMQ調研

一、發展歷程

早期淘寶內部有兩套消息中間件系統:Notify和Napoli。 先有的Notify(至今12歷史),后來因有序場景需求,且恰好當時Kafka開源(2011年),所以參照Kafka的設計理念自研了RocketMQ。 目前Notify和RocketMQ二者的定位如下:

  • RocketQ 主要面向消息有序的場景,能夠提供更大的消息堆積能力,拉模式,消息持久化在磁盤
  • Notify主要面向更加安全可靠地交易類場景,無序、推模式、消息持久化在mysql

RocketMQ發展歷程如下

  • Metaq 1.x 開源社區維護killme2008維護,因為依賴zk掛了,導致上下游服務全網宕機,到了12年基于開源Kafka,直接用java語言翻譯重寫
  • Metaq 2.x 2012年11月上線,淘寶內部使用
  • RocketMQ 3.x 后來一統江湖成為整個阿里系主流MQ。基于公司內部開源共建原則, RocketMQ項目只維護核心功能,且去除了所有其他運行時依賴,核心功能最簡化。每個BU的個性化需求都在RocketMQ項目之上進行深度定制。RocketMQ向其他BU提供的僅僅是Jar包,例如要定制一個Broker,那么只需要依賴rocketmq-broker這個jar包即可,可通過API進行交互,如果定制client,則依賴rocketmq-client這個jar包,對其提供的api進行再封裝。
  • RocketMQ 4.x.x 捐獻給Apache社區,經過一年時間重構孵化成為頂級項目

Metaq改名為RocketMQ,RocketMQ項目做核心功能,淘寶內部其他個性化需求有做定制化開發,如:
com.taobao.metaq v3.0 (為淘寶應用提供消息服務 ) com.alipay.zpullmsg v1.0 (為支付寶應用提供消息服務) com.alibaba.commonmq v1.0 (為 B2B 應用提供消息服務)

 

RocketMQ一共經歷了三代里程碑演進:

  • Notify 為阿里系第一代MQ產品。推模式,數據存儲采用關系型數據庫。
  • Metaq 為阿里系第二代MQ產品。拉模式,自研的專有消息存儲,在日志處理方面參考Kafka,典型代表MetaQ。
  • RocketMQ為阿里系第三代MQ產品。以拉模式為主,兼有推模式,低延遲消息引擎RocketMQ,在二代功能特性的基礎上,為電商金融領域添加了可靠重試、基于文件存儲的分布式事務等特性。使用在了阿里大量的應用上,典型如雙11場景,具有萬億級消息堆積能力。

RocketMQ項目根據開源與商業分成2個版本:

  • Apache RocketMQ是對外開源版
  • 2013年,阿里云ONS(功能相比較更齊全,特別是運維體系完善,例如:運維管控,安全授權,深度培訓等納入商業重中之重)
  • 2015年,Aliware MQ(Message Queue)是RocketMQ的商業版本,是阿里云商用的專業消息中間件,是企業級互聯網架構的核心產品,基于高可用分布式集群技術,搭建了包括發布訂閱、消息軌跡、資源統計、定時(延時)、監控報警等一套完整的消息云服務。

二、系統架構

 系統定位

  • 是一個隊列模型的消息中間件,具有高性能、高可靠、高實時、分布式特點
  • 同時支持Push與Pull方式消費消息
  • 能支撐天貓雙十一海量消息考驗
  • 能夠保證嚴格的消息順序
  • 提供豐富的消息拉取模式
  • 高效的訂閱者水平擴展能力
  • 億級消息堆積能力

四種集群部署方式: 

  • 單master (缺點:broker宕機,服務不可用)
  • 多master無slave (缺點:單臺機器宕機期間,這臺機器上未被消費的消息在機器恢復之前不可訂閱)
  • 多master多slave,異步復制 (缺點:Master 宕機,磁盤損壞情況,可能會丟失少量消息)
  • 多master多slave,同步雙寫(缺點:性能比異步復制模式略低,大約低 10%左右)

生產環境部署都是多主多從。下面以2主2從為例

 

組件角色 

  • Producer:消息發布的角色,支持分布式集群方式部署。與NameServer(隨機)中的其中一個節點建立長鏈接,定期獲取Topic路由信息,并向提供Topic服務的Master建立長鏈接,另外和 Master之間做心跳。Producer通過MQ的負載均衡模塊選擇相應的Broker集群隊列進行消息投遞,投遞的過程支持快速失敗并且低延遲。 
  • Consumer:消息消費的角色,支持分布式集群方式部署。與NameServer(隨機)中的其中一個節點建立長鏈接,定期獲取Topic路由信息,并向提供topic服務的Master、Slave建立長連接 ,由Broker配置訂閱規則。支持以push推,pull拉兩種模式對消息進行消費。同時也支持集群方式和廣播方式的消費,它提供實時消息訂閱機制,可以滿足大多數用戶的需求。
  • NameServer:NameServer是一個非常簡單的Topic路由注冊中心,其角色類似Dubbo中的zookeeper,支持Broker的動態注冊與發現。主要包括兩個功能:Broker管理,NameServer接受Broker集群的注冊信息并且保存下來作為路由信息的基本數據。然后提供心跳檢測機制,檢查Broker是否還存活;路由信息管理,每個NameServer將保存關于Broker集群的整個路由信息和用于客戶端查詢的隊列信息。然后Producer和Conumser通過NameServer就可以知道整個Broker集群的路由信息,從而進行消息的投遞和消費。NameServer通常也是集群的方式部署,各實例間相互不進行信息通訊。Broker是向每一臺NameServer注冊自己的路由信息,所以每一個NameServer實例上面都保存一份完整的路由信息。當某個NameServer因某種原因下線了,Broker仍然可以向其它NameServer同步其路由信息,Producer,Consumer仍然可以動態感知Broker的路由的信息。
  • BrokerServer:Broker主要負責消息的存儲、投遞和查詢以及服務高可用保證,為了實現這些功能,Broker包含了以下5個重要子模塊:

    • Remoting Module:整個Broker的實體,負責處理來自clients端的請求。
    • Client Manager:負責管理客戶端(Producer/Consumer)和維護Consumer的Topic訂閱信息
    • Store Service:提供方便簡單的API接口處理消息存儲到物理硬盤和查詢功能。
    • HA Service:高可用服務,提供Master Broker 和 Slave Broker之間的數據同步功能。
    • Index Service:根據特定的Message key對投遞到Broker的消息進行索引服務,以提供消息的快速查詢。

三、關鍵特性

  1.單機支持1萬以上持久化隊列

  • 順序寫,隨機讀。 consumerQueue是邏輯隊列存儲元數據信息,commitlog負責存儲消息,consumerQueue只存儲消息在commitlog中的位置信息,定長存儲,支持串行方式刷盤。

  2.刷盤策略

  • 同步刷盤
  • 異步刷盤

  二者的區別在于是寫完PageCache直接返回,還是刷盤后返回

  3.消息查詢/消息回溯

  • 支持MessageID和MessageKey查詢。(業務場景:如某個訂單處理失敗,是消息沒收到還是收到處理出錯了)
  • 按照時間來回溯消息,精度毫秒。(業務場景:訂單分析,程序bug,導致今天從某個時間點的消息需要重新開始消費)

  4.消息過濾

  • Broker端(tag的哈希值比對,丟到對應的consumeQueue中) consumer端(直接和tag比)

  5.消息獲取機制

  本質上都是Pull機制(據官方資料顯示其中PushConsumer的實時性接近于push)。

  • PushConsumer: consumer通過長輪詢拉取消息后回調MessageListener接口完成消費,業務只需要完成MessageListener完成業務邏輯即可。(注冊監聽回調,一個線程專門長輪訓從broker端拉消息,push到一個本地可配置隊列)輯即可。(注冊監聽回調,一個線程專門長輪訓從broker端拉消息,push到一個本地可配置隊列) 
  • PullConsumer: 完全由業務系統去控制,定時拉取消息,指定隊列消費,主要由業務控制。

  6.單隊列并行消費

  • 單隊列一批消息拉取到消費端,既可以支持單線程串行有序消費,也可以支持多線程亂序消費提高并發性能,如下圖所示:

 

  采用滑動窗口方式并行消費,多個線程消費,提交offset都是最小offset。

  7.消費負載均衡

都在客戶端實現
Producer端:從NameServer獲取MessageQueue列表,RR選擇具體的消息隊列發送消息。

 

  Consumer端: 從NameServer獲取MessageQueue列表和其他Consumer狀態信息,達到平均消費目的(consumer超過隊列數則處于空閑狀態)

  

   8.順序消息原理

 在RocketMQ中,主要指的是局部順序,即一類消息為滿足順序性,必須 Producer 單線程順序發送,且發送到同一個隊列,這樣 Consumer 就可以按照 Producer 發送 的順序去消費消息。

  • 普通順序消息:Broker重啟,隊列總數發生變化,導致哈希取模后定位隊列變化,導致短暫消息順序不一致。
  • 嚴格順序消息:只要一臺機器不可用,整個集群不可用。(同步雙寫保證)

   

  9.事務支持
  RocketMQ采用了2PC的方案來提交事務消息,同時增加一個補償邏輯來處理二階段超時或者失敗的消息,如下圖所示:

   

  上圖說明了事務消息的大致方案,分為兩個邏輯:正常事務消息的發送及提交、事務消息的補償流程
  事務消息發送及提交:

  1. 發送消息(half消息)
  2. 服務端響應消息寫入結果
  3. 根據發送結果執行本地事務(如果寫入失敗,此時half消息對業務不可見,本地邏輯不執行)
  4. 根據本地事務狀態執行Commit或者Rollback(Commit操作生成消息索引,消息對消費者可見)

  補償流程:

  1. 對沒有Commit/Rollback的事務消息(pending狀態的消息),從服務端發起一次“回查”
  2. Producer收到回查消息,檢查回查消息對應的本地事務的狀態
  3. 根據本地事務狀態,重新Commit或者Rollback
  4. 補償階段用于解決消息Commit或者Rollback發生超時或者失敗的情況。

  10.延時消息
  業務場景:支付曾經提過延時消費需求(對應消費失敗后,延時多久再推送)
  開源版本RocketMQ僅支持定時Level(幾個梯度的延時,5s、10s、1min等) 阿里云的ONS支持定時level,以及制定毫秒級別延時時間

  11.消息失敗重試

  • Producer端:

      Producer 的 send 方法本身支持內部重試,重試邏輯如下:
  (1) 至多重試 3 次
  (2) 如果發送失敗,則輪轉到下一個 Broker
  (3) 這個方法的總耗時時間不超過 sendMsgTimeout設置的值,默認 10s所以,如果本身向 broker 發送消息產生超時異常,就不會再做重試。 再發送失敗由應用層自己做。

  • Consumer端:
    廣播模式:發送失敗的消息丟棄, 廣播模式對于失敗重試代價過高,對整個集群性能會有較大影響,失敗重試功能交由應用處理 集群模式:將消費失敗的消息一條條的發送到broker的重試隊列中去,如果此時依然有發送到重試隊列還是失敗的消息,那就在cosumer的本地線 程
    定時5秒鐘以后重試重新消費消息,再走一次上面的消費流程。

  12.Broker HA機制

  • 同步雙寫:HA 采用同步雙寫方式,主備都寫成功,向應用返回成功。
  • 異步復制:slave啟動一個線程,不斷從master拉取commitlog中的數據,然后異步build出ConsumeQueue數據結構。

  13.死信隊列
  由于某些原因消息無法被正確的投遞,為了確保消息不會被無故的丟棄,一般將其置于一個特殊角色的隊列,這個隊列一般稱之為死信隊列。與此對應的還有一個“回退隊列”的概念,試想如果消費者在消費時發生了異常,那么就不會對這一次消費進行確認(Ack), 進而發生回滾消息的操作之后消息始終會放在隊列的頂部,然后不斷被處理和回滾,導致隊列陷入死循環。為了解決這個問題,可以為每個隊列設置一個回退隊列,它和死信隊列都是為異常的處理提供的一種機制保障。實際情況下,回退隊列的角色可以由死信隊列和重試隊列來扮演。


  14.重試隊列
  重試隊列其實可以看成是一種回退隊列,具體指消費端消費消息失敗時,為防止消息無故丟失而重新將消息回滾到 Broker 中。與回退隊列不同的是重試隊列一般分成多個重試等級,每個重試等級一般也會設置重新投遞延時,重試次數越多投遞延時就越大。舉個例子:消息第一次消費失敗入重試隊列 Q1,Q1 的重新投遞延遲為 5s,在 5s 過后重新投遞該消息;如果消息再次消費失敗則入重試隊列 Q2,Q2 的重新投遞延遲為 10s,在 10s 過后再次投遞該消息。以此類推,重試越多次重新投遞的時間就越久,為此需要設置一個上限,超過投遞次數就入死信隊列。重試隊列與延遲隊列有相同的地方,都是需要設置延遲級別,它們彼此的區別是:延遲隊列動作由內部觸發,重試隊列動作由外部消費端觸發;延遲隊列作用一次,而重試隊列的作用范圍會向后傳遞。

四、不足之處 

  RocketMQ不管系統架構,還是底層存儲都有居多亮點,以此來支撐的眾多強大特性,不可否認也有居多不足之處:

  • 沒有實現自動感知分配的讀寫分離策略,只有當master消費性能過低時(由RocketMQ決定)才會將讀請求分攤到slave上
  • 不支持Master/Slave自動切換。RocketMQ開源版本目前還不支持把Slave自動轉成Master,如果機器資源不足,需要把Slave轉成Master,則要手動停止Slave角色的Broker,更改配置文件,用新的配置文件啟動Broker。商業版本支持自動master/slave主從切換
  • 不支持數據遷移,對服務擴容不太友好,也不靈活。如果服務需要擴容,只能增加服務器節點數了,然后新增queue分配到新節點上。如果新老機器負載不均衡,要么多增加queue到新機器上,要么替換性能較弱的老舊機器
  • 不支持多掛載點。當今硬件發展日新月異,pc服務器性能越來越強大,一個物理機器會掛載很塊多磁盤,但一個RocketMQ實例卻只能讀寫操作一個掛載點數據,想榨干機器資源,操作多掛載點需要部署多實例或依靠docker容器等來實現

 

博客地址引用:http://www.jsfhjj.com/lizherui/p/12655425.html

posted on 2020-04-07 21:43  李志濤  閱讀(...)  評論(...編輯  收藏

最新chease0ldman老人