RocketMQ初步應用架構理論


寫給RocketMQ架構應用入門,內容涉及它的設計機理以及推到出來的應用注意事項,入門人員請看。

稍微涉及技術細節,留以我設計中間件時參考,將來整理深度文檔時會抽取走,入門人員可以無視。

以下RocketMQ簡稱為RQ,理論部分采用版本為3.2.4,測試部分采用版本為3.2.6。

MQ的需求

我們對MQ的需求,相比JMS標准有幾點要求更高:

1. 必須優美靈活地支持集群消費。

2. 盡量支持消息堆積。

3. 服務高可用性和消息可靠性。

4. 有起碼的運維工具做集群管理和服務調整。

其他 提供順序消息、事務、回溯等面向特別場景的功能更好,目前暫不需要。

 

RQ架構

RQ的基本組成包括nameserver、broker、producer、consumer四種節點,前兩種構成服務端,后兩種在客戶端上。

還有其他輔助的進程,不提。

NameServer的基本概念

在沒有NameServer的中間件中,服務端集群就由干活的broker組成 ,其中的實例分主從兩種角色。那么客戶端就要知道,需要連接到哪個地址的broker上去做事情,於是客戶端就需要配置服務端機器的IP地址,如果服務端部署結構復雜,客戶端的配置結構也挺復雜,更討厭的是甚至可能需要客戶端也得更新地址配置。由於有了兩種思路的方案:

一是引入NameServer,負責提供地址。客戶端只需要知道NameServer機器的地址,需要找服務器干活的時候,先問NameServer我該去找哪個服務器。這樣,因為NameServer很簡單而不容易出故障,所以極少發生架構調整。而結構復雜的broker部分,無論怎么調整,客戶端都不用再操心。

RQ 2.x用的是Zookeeper做NameServer,3.x用的是自己搞的獨立服務,不知道為啥,不過代碼貌似不復雜。

二是引入反向代理,就是把服務端地址配置成虛擬IP或域名這種思路,這一個地址背后其實可能是一個集群,客戶端發起請求后,由網絡設施來中轉請求給具體服務器。

兩者各有優劣,綜合使用也挺正常。

NameServer的工作機制

I.NameServer自身之間的機制

可以啟動多個實例,相互獨立,不需要相互通信,可以理解為多機熱備。

II.NameServer與broker之間

Broker啟動后,向指定的一批NameServer發起長連接,此后每隔30s發送一次心跳,心跳內容中包含了所承載的topic信息;NameServer會每隔2分鍾掃描,如果2分鍾內無心跳,就主動斷開連接。

當然如果Broker掛掉,連接肯定也會斷開。

一旦連接斷開,因為是長連接,所以NameServer立刻就會感知有broker掛掉了,於是更新topic與broker的關系。但是,並不會主動通知客戶端。

III.NameServer與客戶端之間

客戶端啟動時,要指定這些NameServer的具體地址。之后隨機與其中一台NameServer保持長連接,如果該NameServer發生了不可用,那么會連接下一個。

連接后會定時查詢topic路由信息,默認間隔是30s,可配置,可編碼指定pollNameServerInteval。

(注意是定時的機制,不是即時查詢,也不是NameServer感知變更后推送,所以這里造成接收消息的實時性問題)。

 

NameServer的部署與應用

I. 運行NameServer

啟動: nohup mqnamesrv &

終止:

sh ./mqshutdown
Useage: mqshutdown broker | namesrv

II. Broker指定NameServer

有幾種方式
1.啟動命令指定
nohup mqbroker -n  "192.168.36.53:9876;192.168.36.80:9876"  &
2.環境變量指定
export NAMESRV_ADDR= 192.168.36.53: 9876 ; 192.168.36.80 : 9876
3.配置指定

 

bin和conf下面的配置文件里面有
root @rocketmq -master1 bin]# sh mqbroker -m
namesrvAddr=

 

III.客戶端指定NameServer

有幾種方式:

1.編碼指定

producer.setNamesrvAddr("192.168.36.53:9876;192.168.36.80:9876");

所以這里指定的並不是broker的主主或主從機器的地址,而是NameServer的地址。

2.java啟動參數

-Drocketmq.namesrv.addr= 192.168.36.53:9876;192.168.36.80:9876
3.環境變量
export NAMESRV_ADDR=192.168.36.53:9876;192.168.36.80:9876
4.服務

客戶端還可以配置這個域名jmenv.tbsite.alipay.net來尋址,就不用指定IP了,這樣NameServer集群可以做熱升級。

該接口具體地址是http://jmenv.tbsite.net:8080/rocketmq/nsaddr

(理論上,最后一種可用性最好;實際上,沒試出來。) 

Broker的機制

I.消息的存儲

1.topic與broker是多對多的關系,一個topic可以做分區配置的,使得可以分散為隊列交付給多個btoker。分區的設計采用的是偏移量做法。
2.Broker是把消息持久化到磁盤文件的,同步刷盤就是寫入后才告知producer成功;異步刷盤是收到消息后就告知producer成功了,之后異步地將消息從內存(PageCache)寫入到磁盤上。
(注意此處涉及到磁盤寫入速度是否大於網卡速度的問題,應用的不好可能造成消息堆積)
3.磁盤空間達到85%之后,不再接收消息,會打印日志並且告知producer發送消息失敗。
4.持久化采取的是ext4文件系統,存儲的數據結構另有其他文檔,運維時需要處理文件目錄時另說。

II.消息的清理

1.每隔10s掃描消息,可以通過cleanResourceInterval配置。
2.每天4點清理消息,可以通過deleteWhen配置。磁盤空間達到閾值時也會啟動。
3.文件保留時長為72小時,可以通過fileReservedTime配置。也就是消息堆積的時限。

III.消息的消費

1.IO用的是文件內存映射方式,性能較高,只會有一個寫,其他的讀。順序寫,隨機讀。

2. 零拷貝原理:

以前使用linux 的sendfile 機制,利用DMA(優點是CPU不參與傳輸),將消息內容直接輸出到sokect 管道,大塊文件傳輸效率高。缺點是只能用BIO。

於是此版本使用的是mmap+write方式,代價是CPU多耗用一些,內存安全問題復雜一些,要避免JVM Crash。

IV.Topic管理

1.客戶端可以配置是否允許自動創建Topic,不允許的話,要先在console上增加此Topic。同時提供管理操作。

V.物理特性

1.CPU:Load高,但使用率低,因為大部分時間在IO Wait。

2. 內存:依舊需要大內存,否則swap會成為瓶頸。

3. 磁盤:IO密集,轉速越高、可靠性越高越好。

VI.broker之間的機制

單機的刷盤機制,雖然保障消息可靠性,但是存在單點故障影響服務可用性,於是有了HA的一些方式。

1.主從雙寫模式,在消息可靠性上依然很高,但是有小問題。

a.master宕機之后,客戶端會得到slave的地址繼續消費,但是不能發布消息。

b.客戶端在與NameServer直接網絡機制的延遲下,會發生一部分消息延遲,甚至要等到master恢復。

c.發現slave有消息堆積后,會令consumer從slave先取數據。

 2 異步復制,消息可靠性上肯定小於主從雙寫

slave的線程不斷從master拉取commitLog的數據,然后異步構建出數據結構。類似mysql的機制。

VII.與consumer之間的機制

1.服務端隊列

topic的一個隊列只會被一個consumer消費,所以該consumer節點最好屬於一個集群。

那么也意味着,comsumer節點的數量>topic隊列的數量,多出來的那些comsumer會閑着沒事干。

舉簡單例子說明:

假設broker有2台機器,topic設置了4個隊列,那么一個broker機器上就承擔2個隊列。

此時消費者所屬的系統,有8台機器,那么運行之后,其中就只有4台機器連接到了MQ服務端的2台broker上,剩下的4台機器是不消費消息的。

所以,此時要想負載均衡,要把topic的分區數量設高。

2.可靠性

consumer與所有關聯的broker保持長連接(包括主從),每隔30s發送心跳,可配置,可以通過heartbeatBrokerInterval配置。

broker每隔10s掃描連接,發現2分鍾內沒有心跳,則關閉連接,並通知該consumer組內其他實例,過來繼續消費該topic。

當然,因為是長連接,所以consumer掛掉也會即時發生上述動作。所以,consumer集群的情況下,消費是可靠的。

而因為consumer與所有broker都持有連接,所以可以兩種角色都訂閱消息,規則由broker來自動決定(比如master掛了之后重啟,要先消費哪一台上的消息)。

3.本地隊列

consumer有線程不斷地從broker拉取消息到本地隊列中,消費線程異步消費。輪詢間隔可指定pullInterval參數,默認0;本地隊列大小可指定pullThresholdForQueue,默認1000。

而不論consumer消費多少個隊列,與一個broker只有一個連接,會有一個任務隊列來維護拉取隊列消息的任務。

4.消費進度上報

定時上報各個隊列的消費情況到broker上,時間間隔可設persistConsumerOffsetInterval。

上述采取的是DefaultMQPushConsumer類做的描述,可見所謂push模式還是定時拉取的,不是所猜測的服務端主動推送。不過拉取采用的是長輪詢的方式,實時性基本等同推送。

 VIII.與producer的機制

1.可靠性

a.producer與broker的網絡機制,與consumer的相同。如果producer掛掉,broker會移除producer的信息,直到它重新連接。

b.producer發送消息失敗,最多可以重試3次,或者不超過10s的超時時間,此時間可通過sendMsgTimeout配置。如果發送失敗,輪轉到下一個broker。

c.producer也可以采用oneway的方式,只負責把數據寫入客戶端機器socket緩沖區。這樣可靠性較低,但是開銷大大減少。(適合采集小數據日志)

2.消息負載

發送消息給broker集群時,是輪流發送的,來保障隊列消息量平均。也可以自定義往哪一個隊列發送。

3.停用機制

當broker重啟的時候,可能導致此時消息發送失敗。於是有命令可以先停止寫權限,40s后producer便不會再把消息往這台broker上發送,從而可以重啟。

sh mqadmin wipeWritePerm -b brokerName -n namesrvAddr

IX.通信機制

1.組件采用的是Netty.4.0.9。

2.協議是他們自己定的新玩意,並不兼容JMS標准。協議具體內容有待我開發C#版客戶端時看詳情。

3.連接是可以復用的,通過header的opaque標示區分。

Broker的集群部署

一句話總結其特征就是:不支持主從自動切換、slave只能讀不能寫,所以故障后必須人工干預恢復負載。

集群方式
運維特點
消息可靠性(master宕機情況)
服務可用性(master宕機情況)
其他特點
備注
一組主主 結構簡單,擴容方便,機器要求低 同步刷盤消息一條都不會丟

整體可用

未被消費的消息無法取得,影響實時性

性能最高 適合消息可靠性最高、實時性低的需求。
一組主從  

異步有毫秒級丟失;

同步雙寫不丟失;

差評,主備不能自動切換,且備機只能讀不能寫,會造成服務整體不可寫。

  不考慮,除非自己提供主從切換的方案。
多組主從(異步復制) 結構復雜,擴容方便 故障時會丟失消息;

整體可用,實時性影響毫秒級別

該組服務只能讀不能寫

性能很高 適合消息可靠性中等,實時性中等的要求。
多組主從(同步雙寫) 結構復雜,擴容方便 不丟消息

整體可用,不影響實時性

該組服務只能讀不能寫。

不能自動切換?

性能比異步低10%,所以實時性也並不比異步方式太高。

適合消息可靠性略高,實時性中等、性能要求不高的需求。

第四種的官方介紹上,比第三種多說了一句:“不支持主從自動切換”。這句話讓我很恐慌,因為第三種也是不支持的,干嘛第四種偏偏多說這一句,難道可用性上比第三種差?

於是做了實驗,證明第三種和第四種可用性是一模一樣的。那么不支持主從切換是什么意思?推斷編寫者是這個意圖:

因為是主從雙寫的,所以數據一致性非常高,那么master掛了之后,slave本是可以立刻切換為主的,這一點與異步復制不一樣。異步復制並沒有這么高的一致性,所以這一句話並不是提醒,而是一個后續功能的備注,可以在雙寫的架構上繼續發展出自動主從切換的功能。

 

架構測試總結:

1.其實根本不用糾結,高要求首選同步雙寫,低要求選主主方案。

2.最好不用一個機器上部署多個broker實例。端口容易沖突,根源問題還沒掌握。

所以,建議采用多台機器,一台起一個broker,構成同步雙寫的架構。也就是官方提供的這種物理和邏輯架構。

注意幾個特征:

a.客戶端是先從NameServer尋址的,得到可用Broker的IP和端口信息,然后自己去連接broker。

b.生產者與所有的master連接,但不能向slave寫入;而消費者與master和slave都建有連接,在不同場景有不同的消費規則。

c.NameServer不去連接別的機器,不主動推消息。

 

客戶端的概念

1.Producer Group

Producer實例的集合。

Producer實例可以是多機器、但機器多進程、單進程中的多對象。Producer可以發送多個Topic。

處理分布式事務時,也需要Producer集群提高可靠性。

2.Consumer Group

Consumer實例 的集合。

Consumer 實例可以是多機器、但機器多進程、單進程中的多對象。

同一個Group中的實例,在集群模式下,以均攤的方式消費;在廣播模式下,每個實例都全部消費。

3.Push Consumer

應用通常向Consumer對象注冊一個Listener接口,一旦收到消息,Consumer對象立刻回調Listener接口方法。所以,所謂Push指的是客戶端內部的回調機制,並不是與服務端之間的機制。

4.Pull Consumer

應用通常主動調用Consumer從服務端拉消息,然后處理。這用的就是短輪詢方式了,在不同情況下,與長輪詢各有優點。

 

發布者和消費者類庫另有文檔,不提。

 

重要問題總結:

1.客戶端選擇推還是拉,其實考慮的是長輪詢和短輪詢的適用場景。

2.服務端首選同步雙寫架構,但依然可能造成故障后30s的消息實時性問題(客戶端機制決定的)。

3.Topic管理,需要先調查客戶端集群機器的數目,合理設置隊列數量之后,再上線。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM