是故勝兵先勝而后求戰(zhàn),敗兵先戰(zhàn)而后求勝。
????????????????????????????????——孫子,《孫子兵法》
大綱


RocketMQ集群部署模式及搭建
1.RocketMQ 中的高可用機(jī)制

? ??RocketMQ 分布式集群是通過 Master 和 Slave 的配合達(dá)到高可用性的。
????Master和Slave 的區(qū)別:在 Broker 的配置文件中,參數(shù) brokerId 的值為 0 表明這個(gè)Broker是Master,大于0表明這個(gè) Broker是Slave,同時(shí)brokerRole參數(shù)也會(huì)說明這個(gè)Broker 是Master還是Slave。
? ??Master角色的 Broker 支持讀和寫,Slave 角色的 Broker 僅支持讀,也就是Producer只能和Master角色的Broker連接寫入消息;Consumer可以連接Master 角色的 Broker,也可以連接Slave角色的Broker 來讀取消息。
2.集群部署模式
(1)單master模式
????也就是只有一個(gè) master 節(jié)點(diǎn),稱不上是集群,一旦這個(gè) master 節(jié)點(diǎn)宕機(jī),那么整個(gè)服務(wù)就不可用。?
(2)多master模式
? ??多個(gè)master節(jié)點(diǎn)組成集群,單個(gè)master節(jié)點(diǎn)宕機(jī)或者重啟對(duì)應(yīng)用沒有影響。
????優(yōu)點(diǎn):所有模式中性能最高(一個(gè) Topic 的可以分布在不同的 master,進(jìn)行橫向拓展)
????在多主多從的架構(gòu)體系下,無論使用客戶端還是管理界面創(chuàng)建主題,一個(gè)主題都會(huì)創(chuàng)建多份隊(duì)列在多主中(默認(rèn)是 4 個(gè)的話,雙主就會(huì)有 8 個(gè)隊(duì)列,每臺(tái)主 4 個(gè)隊(duì)列,所以雙主可以提高性能,一個(gè) Topic 的分布在不同的 master,方便進(jìn)行橫向拓展。
????缺點(diǎn):單個(gè)master 節(jié)點(diǎn)宕機(jī)期間,未被消費(fèi)的消息在節(jié)點(diǎn)恢復(fù)之前不可用,消息的實(shí)時(shí)性就受到影響。
(3)多master多slave異步復(fù)制模式
? ??而從節(jié)點(diǎn)(Slave)就是復(fù)制主節(jié)點(diǎn)的數(shù)據(jù),對(duì)于生產(chǎn)者完全感知不到,對(duì)于消費(fèi)者正常情況下也感知不到。(只有當(dāng) Master 不可用或者繁忙的時(shí)候, Consumer 會(huì)被自動(dòng)切換到從 Slave 讀。)
????在多 master 模式的基礎(chǔ)上,每個(gè) master 節(jié)點(diǎn)都有至少一個(gè)對(duì)應(yīng)的 slave。master 節(jié)點(diǎn)可讀可寫,但是 slave 只能讀不能寫,類似于 mysql的主備模式。
????優(yōu)點(diǎn): 一般情況下都是 master 消費(fèi),在 master 宕機(jī)或超過負(fù)載時(shí),消費(fèi)者可以從 slave 讀取消息,消息的實(shí)時(shí)性不會(huì)受影響,性能幾乎和多master一樣。
? ??缺點(diǎn):使用異步復(fù)制的同步方式有可能會(huì)有消息丟失的問題。(Master 宕機(jī)后,生產(chǎn)者發(fā)送的消息沒有消費(fèi)完,同時(shí)到 Slave 節(jié)點(diǎn)的數(shù)據(jù)也沒有同步完)
(4)多master多slave主從同步復(fù)制+異步刷盤(最優(yōu)推薦)
? ??優(yōu)點(diǎn):主從同步復(fù)制模式能保證數(shù)據(jù)不丟失。
?????缺點(diǎn):發(fā)送單個(gè)消息響應(yīng)時(shí)間會(huì)略長(zhǎng),性能相比異步復(fù)制低 10%左右。?
????對(duì)數(shù)據(jù)要求較高的場(chǎng)景,主從同步復(fù)制方式,保存數(shù)據(jù)熱備份,通過異步刷盤方式,保證 rocketMQ 高吞吐量。
(5)Dlegder(不推薦)
? ??在 RocketMQ4.5 版本之后推出了Dlegder模式,但是這種模式一直存在嚴(yán)重的 BUG,同時(shí)性能有可能有問題,包括升級(jí)到了 4.8 的版本后也一樣,所以目前不講這種模式。(類似于Zookeeper的集群選舉模式)
3.刷盤與主從同步
? ??生產(chǎn)時(shí)首先將消息寫入到 MappedFile,內(nèi)存映射文件,然后根據(jù)刷盤策略刷寫到磁盤。
????大致的步驟可以理解成使用 MMAP 中的 MappedByteBuffer 中實(shí)際用 flip()。

? ??RocketMQ的刷盤是把消息存儲(chǔ)到磁盤上的,這樣既能保證斷電后恢復(fù), 又可以讓存儲(chǔ)的消息量超出內(nèi)存的限制。RocketMQ為了提高性能,會(huì)盡可能地保證磁盤的順序?qū)憽O⒃谕ㄟ^Producer寫入RocketMQ 的時(shí)候,有兩種寫磁盤方式,同步刷盤和異步刷盤。
(1)同步刷盤
? ??SYNC_FLUSH(同步刷盤):生產(chǎn)者發(fā)送的每一條消息都在保存到磁盤成功后才返回告訴生產(chǎn)者成功。這種方式不會(huì)存在消息丟失的問 題,但是有很大的磁盤 IO 開銷,性能有一定影響。
(2)異步刷盤
? ??ASYNC_FLUSH(異步刷盤):生產(chǎn)者發(fā)送的每一條消息并不是立即保存到磁盤,而是暫時(shí)緩存起來,然后就返回生產(chǎn)者成功。隨后再異步的將緩存數(shù)據(jù)保存到磁盤,有兩種情況:1是定期將緩存中更新的數(shù)據(jù)進(jìn)行刷盤,2是當(dāng)緩存中更新的數(shù)據(jù)條數(shù)達(dá)到某一設(shè)定值后進(jìn)行刷盤。這種異步的方式會(huì)存在消息丟失(在還未來得及同步到磁盤的時(shí)候宕機(jī)),但是性能很好。默認(rèn)是這種模式。

? ??4.8.0 版本中默認(rèn)值下是異步刷盤,如下圖:

(3)主從同步復(fù)制
? ??集群環(huán)境下需要部署多個(gè)Broker,Broker分為兩種角色:一種是 master,即可以寫也可以讀,其brokerId=0,只能有一個(gè);另外一種是slave,只允許讀,其brokerId為非0。一個(gè)master與多個(gè)slave通過指定相同的brokerClusterName被歸為一個(gè) broker set(broker 集)。通常生產(chǎn)環(huán)境中,我們至少需要2個(gè)broker set。Slave是復(fù)制master的數(shù)據(jù)。一個(gè)Broker組有 Master和Slave,消息需要從Master復(fù)制到Slave上,有同步和異步兩種復(fù)制方式。
????主從同步復(fù)制方式(Sync Broker):生產(chǎn)者發(fā)送的每一條消息都至少同步復(fù)制到一個(gè) slave 后才返回告訴生產(chǎn)者成功,即“同步雙寫”。
????在同步復(fù)制方式下,如果 Master 出故障, Slave 上有全部的備份數(shù)據(jù),容易恢復(fù),但是同步復(fù)制會(huì)增大數(shù)據(jù)寫入延遲,降低系統(tǒng)吞吐量。
(4)主從異步復(fù)制
? ??主從異步復(fù)制方式(Async Broker):生產(chǎn)者發(fā)送的每一條消息只要寫入master就返回告訴生產(chǎn)者成功。然后再“異步復(fù)制”到slave。
????在異步復(fù)制方式下,系統(tǒng)擁有較低的延遲和較高的吞吐量,但是如果Master出了故障,有些數(shù)據(jù)因?yàn)闆]有被寫入Slave,有可能會(huì)丟失;
????同步復(fù)制和異步復(fù)制是通過Broker配置文件里的brokerRole參數(shù)進(jìn)行設(shè)置的,這個(gè)參數(shù)可以被設(shè)置成 ASYNC_MASTER、 SYNC_MASTER、SLAVE 三個(gè)值中的一個(gè)。
4.配置參數(shù)及意義
? ??brokerId=0 代表主
????brokerId=1 代表從(大于 0 都代表從)
????brokerRole=SYNC_MASTER 同步復(fù)制(主從)
????brokerRole=ASYNC_MASTER 異步復(fù)制(主從)
????flushDiskType=SYNC_FLUSH 同步刷盤
????flushDiskType=ASYNC_FLUSH 異步刷盤
5.搭建雙主雙從同步復(fù)制+異步刷盤
(1)NameServer集群
? ? 192.168.1.1
? ? 192.168.1.2
(2)Broker服務(wù)器
? ??192.168.1.1? ? ? ? --MasterA
? ? 192.168.1.2? ? ? ? --MasterB
? ? 192.168.1.3? ? ? ? --SlaveA
? ? 192.168.1.4? ? ? ? --SlaveB
(3)配置文件
? ??注意,因?yàn)镽ocketMQ使用外網(wǎng)地址,所以配置文件(MQ文件夾/conf/2m-2s-sync/)需要修改(同時(shí)修改nameserver地址為集群地址):
????注意,如果機(jī)器內(nèi)存不夠,建議把啟動(dòng)時(shí)的堆內(nèi)存改小,具體見《RocketMQ 的安裝.docx》中 --- 3、RocketMQ 在 Linux 下的安裝/注意事項(xiàng)
????192.168.1.1????????????????------主 A
????broker-a.properties????增加: brokerIP1=192.168.1.1
????????????????????????????????????????namesrvAddr=192.168.1.1:9876;192.168.1.2:9876

? ??192.168.1.2? ? ? ? ? ? ? ? ?????------主 B
????broker-b.properties? ? ? ? ? 增加: brokerIP1=192.168.1.2
????????????????????????????????????????????namesrvAddr=192.168.1.1:9876;192.168.1.2:9876

????192.168.1.3? ? ? ? ? ? ? ? ? ? ? ? ? ?------從 A
????broker-a-s.properties????????????增加:brokerIP1=192.168.1.3
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? namesrvAddr=192.168.1.1:9876;192.168.1.2:9876

? ??192.168.1.4? ? ? ? ? ? ? ? ? ? ?????------從 B
????broker-b-s.properties? ? ? ? ? 增加:brokerIP1=192.168.1.4
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? namesrvAddr=192.168.1.1:9876;192.168.1.2:9876

? ??不管是主還是從,如果屬于一個(gè)集群,使用相同的 brokerClusterName 名稱

(4)啟動(dòng)步驟
啟動(dòng) NameServer
? ??(記得關(guān)閉防火墻或者要開通 9876 端口)
????1.啟動(dòng)NameServer集群,這里使用192.168.1.1 和 192.168.1.2兩臺(tái)作為集群即可。
????1)在機(jī)器 A,啟動(dòng)第 1 臺(tái) NameServer: 102 服務(wù)器進(jìn)入至‘MQ 文件夾/bin’下:然后執(zhí)行‘nohup sh mqnamesrv &’
????查看日志的命令:tail -f ~/logs/rocketmqlogs/namesrv.log

? ??2) 在機(jī)器 B,啟動(dòng)第2臺(tái)NameServer: 103 服務(wù)器進(jìn)入至‘MQ文件夾/bin’下:然后執(zhí)行‘nohup sh mqnamesrv &’
????查看日志的命令:tail -f ~/logs/rocketmqlogs/namesrv.log

啟動(dòng) Broker
? ??2.啟動(dòng)雙主雙從同步集群,順序是先啟動(dòng)主,然后啟動(dòng)從。
????3)啟動(dòng)主A: 102 服務(wù)器進(jìn)入至‘MQ 文件夾/bin’下:執(zhí)行以下命令(autoCreateTopicEnable=true 測(cè)試環(huán)境開啟,生產(chǎn)環(huán)境建議關(guān)閉):
????nohup sh mqbroker -c ../conf/2m-2s-sync/broker-a.properties ????autoCreateTopicEnable=true &
????查看日志的命令:tail -f ~/logs/rocketmqlogs/broker.log

? ??4)啟動(dòng)主 B: 103 服務(wù)器進(jìn)入至‘MQ 文件夾\bin’下:執(zhí)行以下命令:nohup sh mqbroker -c ../conf/2m-2s-sync/broker-b.properties ????autoCreateTopicEnable=true &
????查看日志的命令:tail -f ~/logs/rocketmqlogs/broker.log
? ??5)啟動(dòng)從 A: 104 服務(wù)器進(jìn)入至‘MQ 文件夾\bin’下:執(zhí)行以下命令:nohup sh mqbroker -c ../conf/2m-2s-sync/broker-a-s.properties autoCreateTopicEnable=true &
????查看日志的命令:tail -f ~/logs/rocketmqlogs/broker.log
? ??6)啟動(dòng)從 B: 105 服務(wù)器進(jìn)入至‘MQ 文件夾\bin’下:執(zhí)行以下命令:nohup sh mqbroker -c ../conf/2m-2s-sync/broker-b-s.properties autoCreateTopicEnable=true &
????查看日志的命令:tail -f ~/logs/rocketmqlogs/broker.log
? ??每臺(tái)服務(wù)器查看日志:tail -f ~/logs/rocketmqlogs/broker.log
如果是要啟動(dòng)控制臺(tái),則需要重新打包:
進(jìn)入‘\rocketmq-console\src\main\resources’文件夾,打開‘a(chǎn)pplication.properties’進(jìn)行配置。(多個(gè) NameServer 使用;分隔)
rocketmq.config.namesrvAddr=192.168.1.1:9876;192.168.1.2:9876
進(jìn)入‘\rocketmq-externals\rocketmq-console’文件夾,執(zhí)行‘mvn clean package -Dmaven.test.skip=true’,編譯生成。
在把編譯后的 jar 包丟上服務(wù)器:
nohup java -jar rocketmq-console-ng-2.0.0.jar &
進(jìn)入控制臺(tái) http://192.168.1.1:8089/#/cluster
集群搭建成功。


消息生產(chǎn)的高可用機(jī)制

? ??在創(chuàng)建Topic的時(shí)候,把Topic的多個(gè)Message Queue 創(chuàng)建在多個(gè)Broker組上(相同Broker名稱,不同brokerId的機(jī)器組成一個(gè)Broker組),這樣當(dāng)一個(gè)Broker組的Master不可用后,其他組的Master仍然可用,Producer仍然可以發(fā)送消息。
????RocketMQ目前不支持把Slave自動(dòng)轉(zhuǎn)成Master,如果機(jī)器資源不足, 需要把Slave轉(zhuǎn)成Master,則要手動(dòng)停止Slave角色的 Broker,更改配置文件,用新的配置文件啟動(dòng)Broker。
1.高可用消息生產(chǎn)流程

????1)TopicA 創(chuàng)建在雙主中,BrokerA 和 BrokerB 中,每一個(gè) Broker 中有 4 個(gè)隊(duì)列。
????2)選擇隊(duì)列是,默認(rèn)是使用輪訓(xùn)的方式,比如發(fā)送一條消息 A 時(shí),選擇 BrokerA 中的 Q4。
????3)如果發(fā)送成功,消息 A 發(fā)結(jié)束。
????4)如果消息發(fā)送失敗,默認(rèn)會(huì)采用重試機(jī)制。
????retryTimesWhenSendFailed 同步模式下內(nèi)部嘗試發(fā)送消息的最大次數(shù) 默認(rèn)值是 2。
????retryTimesWhenSendAsyncFailed 異步模式下內(nèi)部嘗試發(fā)送消息的最大次數(shù) 默認(rèn)值是 2。

????5)如果發(fā)生了消息發(fā)送失敗,這里有一個(gè)規(guī)避策略(默認(rèn)配置):
????5.1)默認(rèn)不啟用 Broker 故障延遲機(jī)制(規(guī)避策略):如果是 BrokerA 宕機(jī),上一次路由選擇的是 BrokerA 中的 Q4,那么再次重發(fā)的隊(duì)列選擇是 BrokerA中的 Q1。但是這里的問題就是消息發(fā)送很大可能再次失敗,引發(fā)再次重復(fù)失敗,帶來不必要的性能損耗。
????注意,這里的規(guī)避僅僅只針對(duì)消息重試,例如,在一次消息發(fā)送過程中如果遇到消息發(fā)送失敗,規(guī)避 broekr-a,但是在下一次消息發(fā)送時(shí),即再次調(diào)用DefaultMQProducer的send方法發(fā)送消息時(shí),還是會(huì)選擇 broker-a 的消息進(jìn)行發(fā)送,只有繼續(xù)發(fā)送失敗后,重試時(shí)再次規(guī)避 broker-a。
? ??為什么會(huì)默認(rèn)這么設(shè)計(jì)?
????1) 某一時(shí)間段,從 NameServer 中讀到的路由中包含了不可用的主機(jī)。
????2)不正常的路由信息也只是一個(gè)短暫的時(shí)間而已。
????生產(chǎn)者每隔 30s 更新一次路由信息,而 NameServer 認(rèn)為 broker 不可用需要經(jīng)過 120s。

? ??所以生產(chǎn)者要發(fā)送時(shí)認(rèn)為 broker 不正常(從 NameServer 拿到)和實(shí)際 Broker 不正常有延遲。
? ??5.2)啟用 Broker 故障延遲機(jī)制:代碼如下

????開啟延遲規(guī)避機(jī)制,一旦消息發(fā)送失敗(不是重試的)會(huì)將broker-a“悲觀”地認(rèn)為在接下來的一段時(shí)間內(nèi)該 Broker 不可用,在為未來某一段時(shí)間內(nèi)所有的客戶端不會(huì)向該Broker發(fā)送消息。這個(gè)延遲時(shí)間就是通過notAvailableDuration、latencyMax共同計(jì)算的,就首先先計(jì)算本次消息發(fā)送失敗所耗的時(shí)延,然后對(duì)應(yīng)latencyMax中哪個(gè)區(qū)間,即計(jì)算在latencyMax的下標(biāo),然后返回 notAvailableDuration同一個(gè)下標(biāo)對(duì)應(yīng)的延遲值。
????這個(gè)里面涉及到一個(gè)算法,源碼部分進(jìn)行詳細(xì)講解。
????比如:在發(fā)送失敗后,在接下來的固定時(shí)間(比如 5 分鐘)內(nèi),發(fā)生錯(cuò)誤的 BrokeA 中的隊(duì)列將不再參加隊(duì)列負(fù)載,發(fā)送時(shí)只選擇BrokerB服務(wù)器上的隊(duì)列。
????如果所有的 Broker 都觸發(fā)了故障規(guī)避,并且Broker只是那一瞬間壓力大,那豈不是明明存在可用的 Broker,但經(jīng)過你這樣規(guī)避,反倒是沒有 Broker可用來,那豈不是更糟糕了。所以 RocketMQ 默認(rèn)不啟用 Broker 故障延遲機(jī)制。
消息消費(fèi)的高可用機(jī)制
1.主從的高可用原理
? ??在 Consumer 的配置文件中,并不需要設(shè)置是從 Master 讀還是從 Slave 讀,當(dāng) Master 不可用或者繁忙的時(shí)候,Consumer 會(huì)被自動(dòng)切換到從 Slave 讀。 有了自動(dòng)切換 Consumer 這種機(jī)制,當(dāng)一個(gè) Master 角色的機(jī)器出現(xiàn)故障后,Consumer 仍然可以從 Slave 讀取消息,不影響Consumer 程序。這就達(dá)到了消費(fèi)端的高可用性。
????Master 不可用這個(gè)很容易理解,那什么是 Master 繁忙呢?
? ??這個(gè)繁忙其實(shí)是 RocketMQ 服務(wù)器的內(nèi)存不夠?qū)е碌摹?/p>
? ??源碼分析:org.apache.rocketmq.store. DefaultMessageStore#getMessage 方法?

????當(dāng)前需要拉取的消息已經(jīng)超過常駐內(nèi)存的大小,表示主服務(wù)器繁忙,此時(shí)才建議從從服務(wù)器拉取。
2.消息消費(fèi)的重試
????消費(fèi)端如果發(fā)生消息失敗,沒有提交成功,消息默認(rèn)情況下會(huì)進(jìn)入重試隊(duì)列中。

? ??注意重試隊(duì)列的名字其實(shí)是跟消費(fèi)群組有關(guān),不是主題,因?yàn)橐粋€(gè)主題可以有多個(gè)群組消費(fèi),所以要注意。

(1)順序消息的重試
????對(duì)于順序消息,當(dāng)消費(fèi)者消費(fèi)消息失敗后,消息隊(duì)列 RocketMQ 會(huì)自動(dòng)不斷進(jìn)行消息重試(每次間隔時(shí)間為 1 秒),這時(shí),應(yīng)用會(huì)出現(xiàn)消息消費(fèi)被阻塞的情況。因此,在使用順序消息時(shí),務(wù)必保證應(yīng)用能夠及時(shí)監(jiān)控并處理消費(fèi)失敗的情況,避免阻塞現(xiàn)象的發(fā)生。
????所以玩順序消息時(shí)。consume消費(fèi)消息失敗時(shí),不能返回reconsume——later,這樣會(huì)導(dǎo)致亂序,應(yīng)該返回suspend_current_queue_a_moment,意思是先等一會(huì),一會(huì)兒再處理這批消息,而不是放到重試隊(duì)列里。
(2)無須消息的重試
? ??對(duì)于無序消息(普通、定時(shí)、延時(shí)、事務(wù)消息),當(dāng)消費(fèi)者消費(fèi)消息失敗時(shí),您可以通過設(shè)置返回狀態(tài)達(dá)到消息重試的結(jié)果。無序消息的重試只針對(duì) 集群消費(fèi)方式生效;廣播方式不提供失敗重試特性,即消費(fèi)失敗后,失敗消息不再重試,繼續(xù)消費(fèi)新的消息。
(3)重試次數(shù)

????如果消息重試 16 次后仍然失敗,消息將不再投遞。如果嚴(yán)格按照上述重試時(shí)間間隔計(jì)算,某條消息在一直消費(fèi)失敗的前提下,將會(huì)在接下來的 4 小時(shí) 46 分鐘之內(nèi)進(jìn)行 16 次重試,超過這個(gè)時(shí)間范圍消息將不再重試投遞。
????注意: 一條消息無論重試多少次,這些重試消息的 Message ID 不會(huì)改變。
(4)重試配置
? ??????集群消費(fèi)方式下,消息消費(fèi)失敗后期望消息重試,需要在消息監(jiān)聽器接口的實(shí)現(xiàn)中明確進(jìn)行配置(三種方式任選一種):?
>返回 RECONSUME_LATER (推薦)
>返回 Null
>拋出異常



? ??集群消費(fèi)方式下,消息失敗后期望消息不重試,需要捕獲消費(fèi)邏輯中可能拋出的異常,最終返回 CONSUME_SUCCESS,此后這條消息將不會(huì)再重試。
(5)自定義消息最大重試次數(shù)
? ??消息隊(duì)列 RocketMQ允許Consumer啟動(dòng)的時(shí)候設(shè)置最大重試次數(shù),重試時(shí)間間隔將按照如下策略:?
? ? >最大重試次數(shù)小于等于 16 次,則重試時(shí)間間隔同上表描述。
? ? >最大重試次數(shù)大于 16 次,超過 16 次的重試時(shí)間間隔均為每次 2 小時(shí)。

? ??消息最大重試次數(shù)的設(shè)置對(duì)相同Group ID下的所有Consumer實(shí)例有效。
????如果只對(duì)相同Group ID下兩個(gè)Consumer實(shí)例中的其中一個(gè)設(shè)置了MaxReconsumeTimes,那么該配置對(duì)兩個(gè) Consumer 實(shí)例均生效。
????配置采用覆蓋的方式生效,即最后啟動(dòng)的Consumer實(shí)例會(huì)覆蓋之前的啟動(dòng)實(shí)例的配置。
3.死信隊(duì)列
? ??當(dāng)一條消息初次消費(fèi)失敗,消息隊(duì)列RocketMQ會(huì)自動(dòng)進(jìn)行消息重試;達(dá)到最大重試次數(shù)后,若消費(fèi)依然失敗,則表明消費(fèi)者在正常情況下無法正 確地消費(fèi)該消息,此時(shí),消息隊(duì)列RocketMQ不會(huì)立刻將消息丟棄,而是將其發(fā)送到該消費(fèi)者對(duì)應(yīng)的特殊隊(duì)列中。
????在消息隊(duì)列RocketMQ中,這種正常情況下無法被消費(fèi)的消息稱為死信消息(Dead-Letter Message),存儲(chǔ)死信消息的特殊隊(duì)列稱為死信隊(duì)列(Dead-Letter Queue)。
(1)死信特性
死信消息具有以下特性:
>不會(huì)再被消費(fèi)者正常消費(fèi)。
>有效期與正常消息相同,均為 3 天,3 天后會(huì)被自動(dòng)刪除。因此,請(qǐng)?jiān)谒佬畔a(chǎn)生后的 3 天內(nèi)及時(shí)處理。
死信隊(duì)列具有以下特性:
>不會(huì)再被消費(fèi)者正常消費(fèi)。
>一個(gè)死信隊(duì)列對(duì)應(yīng)一個(gè) Group ID, 而不是對(duì)應(yīng)單個(gè)消費(fèi)者實(shí)例。
>如果一個(gè) Group ID 未產(chǎn)生死信消息,消息隊(duì)列 RocketMQ 不會(huì)為其創(chuàng)建相應(yīng)的死信隊(duì)列。
>一個(gè)死信隊(duì)列包含了對(duì)應(yīng) Group ID 產(chǎn)生的所有死信消息,不論該消息屬于哪個(gè) Topic。
(2)查看死信消息
? ??在控制臺(tái)查詢出現(xiàn)死信隊(duì)列的主題信息

? ??在消息界面根據(jù)主題查詢死信消息
????選擇重新發(fā)送消息
????一條消息進(jìn)入死信隊(duì)列,意味著某些因素導(dǎo)致消費(fèi)者無法正常消費(fèi)該消息,因此,通常需要您對(duì)其進(jìn)行特殊處理。排查可疑因素并解決問題后,可以在消息隊(duì)列 RocketMQ 控制臺(tái)重新發(fā)送該消息,讓消費(fèi)者重新消費(fèi)一次。
RocketMQ中的負(fù)載均衡
1.Producer負(fù)載均衡
????Producer 端,每個(gè)實(shí)例在發(fā)消息的時(shí)候,默認(rèn)會(huì)輪詢所有的 message queue 發(fā)送,以達(dá)到讓消息平均落在不同的 queue 上。而由于 queue 可以散落在不同的 broker,所以消息就發(fā)送到不同的 broker 下,如下圖:

? ??發(fā)布方會(huì)把第一條消息發(fā)送至 Queue 0,然后第二條消息發(fā)送至 Queue 1,以此類推。
2.Consumer負(fù)載均衡
(1)集群模式
????在集群消費(fèi)模式下,每條消息只需要投遞到訂閱這個(gè) topic 的 Consumer Group 下的一個(gè)實(shí)例即可。RocketMQ 采用主動(dòng)拉取的方式拉取并消費(fèi)消息,在拉取的時(shí)候需要明確指定拉取哪一條 message queue。
????而每當(dāng)實(shí)例的數(shù)量有變更,都會(huì)觸發(fā)一次所有實(shí)例的負(fù)載均衡,這時(shí)候會(huì)按照 queue 的數(shù)量和實(shí)例的數(shù)量平均分配 queue 給每個(gè)實(shí)例。
????默認(rèn)的分配算法是 AllocateMessageQueueAveragely。
? ??還有另外一種平均的算法是 AllocateMessageQueueAveragelyByCircle,也是平均分?jǐn)偯恳粭l queue,只是以環(huán)狀輪流分 queue 的形式 。
????如下圖:

? ??需要注意的是,集群模式下,queue 都是只允許分配只一個(gè)實(shí)例,這是由于如果多個(gè)實(shí)例同時(shí)消費(fèi)一個(gè) queue 的消息,由于拉取哪些消息是 consumer 主動(dòng)控制的,那樣會(huì)導(dǎo)致同一個(gè)消息在不同的實(shí)例下被消費(fèi)多次,所以算法上都是一個(gè) queue 只分給一個(gè) consumer 實(shí)例,一個(gè) consumer 實(shí)例可以允許同時(shí)分到不同的 queue。
????通過增加 consumer 實(shí)例去分?jǐn)?queue 的消費(fèi),可以起到水平擴(kuò)展的消費(fèi)能力的作用。而有實(shí)例下線的時(shí)候,會(huì)重新觸發(fā)負(fù)載均衡,這時(shí)候原來分配到的queue 將分配到其他實(shí)例上繼續(xù)消費(fèi)。
????但是如果 consumer 實(shí)例的數(shù)量比 message queue 的總數(shù)量還多的話,多出來的 consumer 實(shí)例將無法分到 queue,也就無法消費(fèi)到消息,也就無法起到分?jǐn)傌?fù)載的作用了。所以需要控制讓 queue 的總數(shù)量大于等于 consumer 的數(shù)量。
(2)廣播模式
????由于廣播模式下要求一條消息需要投遞到一個(gè)消費(fèi)組下面所有的消費(fèi)者實(shí)例,所以也就沒有消息被分?jǐn)傁M(fèi)的說法。?
????在實(shí)現(xiàn)上,其中一個(gè)不同就是在 consumer 分配 queue 的時(shí)候,所有 consumer 都分到所有的 queue。
我是嬈疆_蚩夢(mèng),讓堅(jiān)持成為一種習(xí)慣,感謝各位大佬的:點(diǎn)贊、收藏和評(píng)論,我們下期見!