RocketMQ初步應用架構理論

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

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

如下RocketMQ簡稱爲RQ,理論部分採用版本爲3.2.4,測試部分採用版本爲3.2.6。mysql

MQ的需求

咱們對MQ的需求,相比JMS標準有幾點要求更高:linux

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

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管理,須要先調查客戶端集羣機器的數目,合理設置隊列數量以後,再上線。

相關文章
相關標籤/搜索