RabbitMQ實(shí)戰(zhàn)(三)-高級(jí)特性

0 相關(guān)源碼

1 你將學(xué)到

  • 如何保證消息百分百投遞成功
  • 冪等性
  • 如何避免海量訂單生成時(shí)消息的重復(fù)消費(fèi)
  • Confirm確認(rèn)消息、Return返回消息
  • 自定義消費(fèi)者
  • 消息的ACK與重回隊(duì)列
  • 限流
  • TTL
  • 死信隊(duì)列

2 保證消息的百分百投遞成功

2.1 Producer 的可靠性投遞

2.1.1 要求

  • 保證消息的成功發(fā)出
  • 保證MQ節(jié)點(diǎn)的成功接收
  • 發(fā)送端收到MQ節(jié)點(diǎn)(Broker) 確認(rèn)應(yīng)答
  • 完善的消息補(bǔ)償機(jī)制

在實(shí)際生產(chǎn)中下硕,很難保障前三點(diǎn)的完全可靠丁逝,比如在極端的環(huán)境中,生產(chǎn)者發(fā)送消息失敗了梭姓,發(fā)送端在接受確認(rèn)應(yīng)答時(shí)突然發(fā)生網(wǎng)絡(luò)閃斷等等情況霜幼,很難保障可靠性投遞,所以就需要有第四點(diǎn)完善的消息補(bǔ)償機(jī)制誉尖。

2.1.2 解決方案

2.1.2.1 方案一:消息信息落庫(kù),對(duì)消息狀態(tài)進(jìn)行打標(biāo)(常見(jiàn)方案)

將消息持久化到DB并設(shè)置狀態(tài)值,收到Consumer的應(yīng)答就改變當(dāng)前記錄的狀態(tài).
再輪詢(xún)重新發(fā)送沒(méi)接收到應(yīng)答的消息,注意這里要設(shè)置重試次數(shù).

方案流程圖
image
方案實(shí)現(xiàn)流程

比如我下單成功
step1 - 對(duì)訂單數(shù)據(jù)入BIZ DB訂單庫(kù),并對(duì)因此生成的業(yè)務(wù)消息入MSG DB消息庫(kù)

此處由于采用了兩個(gè)數(shù)據(jù)庫(kù),需要兩次持久化操作,為了保證數(shù)據(jù)的一致性,有人可能就想著采用分布式事務(wù),但在大廠(chǎng)實(shí)踐中,基本都是采用補(bǔ)償機(jī)制!

這里一定要保證step1 中消息都存儲(chǔ)成功了罪既,沒(méi)有出現(xiàn)任何異常情況,然后生產(chǎn)端再進(jìn)行消息發(fā)送。如果失敗了就進(jìn)行快速失敗機(jī)制

對(duì)業(yè)務(wù)數(shù)據(jù)和消息入庫(kù)完畢就進(jìn)入
setp2 - 發(fā)送消息到 MQ 服務(wù)上琢感,如果一切正常無(wú)誤消費(fèi)者監(jiān)聽(tīng)到該消息丢间,進(jìn)入

step3 - 生產(chǎn)端有一個(gè)Confirm Listener,異步監(jiān)聽(tīng)Broker回送的響應(yīng),從而判斷消息是否投遞成功

  • step4 - 如果成功,去數(shù)據(jù)庫(kù)查詢(xún)?cè)撓?并將消息狀態(tài)更新為1
  • step5 - 如果出現(xiàn)意外情況,消費(fèi)者未接收到或者 Listener 接收確認(rèn)時(shí)發(fā)生網(wǎng)絡(luò)閃斷驹针,導(dǎo)致生產(chǎn)端的Listener就永遠(yuǎn)收不到這條消息的confirm應(yīng)答了千劈,也就是說(shuō)這條消息的狀態(tài)就一直為0了,這時(shí)候就需要用到我們的分布式定時(shí)任務(wù)來(lái)從 MSG 數(shù)據(jù)庫(kù)抓取那些超時(shí)了還未被消費(fèi)的消息牌捷,重新發(fā)送一遍
    此時(shí)我們需要設(shè)置一個(gè)規(guī)則墙牌,比如說(shuō)消息在入庫(kù)時(shí)候設(shè)置一個(gè)臨界值timeout,5分鐘之后如果還是0的狀態(tài)那就需要把消息抽取出來(lái)暗甥。這里我們使用的是分布式定時(shí)任務(wù)喜滨,去定時(shí)抓取DB中距離消息創(chuàng)建時(shí)間超過(guò)5分鐘的且狀態(tài)為0的消息。

step6 - 把抓取出來(lái)的消息進(jìn)行重新投遞(Retry Send)撤防,也就是從第二步開(kāi)始繼續(xù)往下走

step7 - 當(dāng)然有些消息可能就是由于一些實(shí)際的問(wèn)題無(wú)法路由到Broker虽风,比如routingKey設(shè)置不對(duì),對(duì)應(yīng)的隊(duì)列被誤刪除了寄月,那么這種消息即使重試多次也仍然無(wú)法投遞成功辜膝,所以需要對(duì)重試次數(shù)做限制,比如限制3次漾肮,如果投遞次數(shù)大于三次厂抖,那么就將消息狀態(tài)更新為2,表示這個(gè)消息最終投遞失敗,然后通過(guò)補(bǔ)償機(jī)制克懊,人工去處理忱辅。實(shí)際生產(chǎn)中,這種情況還是比較少的谭溉,但是你不能沒(méi)有這個(gè)補(bǔ)償機(jī)制墙懂,要不然就做不到可靠性了。

思考:該方案在高并發(fā)的場(chǎng)景下是否合適

對(duì)于第一種方案扮念,我們需要做兩次數(shù)據(jù)庫(kù)的持久化操作损搬,在高并發(fā)場(chǎng)景下顯然數(shù)據(jù)庫(kù)存在著性能瓶頸.

其實(shí)在我們的核心鏈路中只需要對(duì)業(yè)務(wù)進(jìn)行入庫(kù)就可以了,消息就沒(méi)必要先入庫(kù)了柜与,我們可以做消息的延遲投遞巧勤,做二次確認(rèn),回調(diào)檢查旅挤。下面然我們看方案二

2.1.2.2 消息延遲投遞,兩次確認(rèn),回調(diào)檢查(大規(guī)模海量數(shù)據(jù)方案)

大廠(chǎng)經(jīng)典實(shí)現(xiàn)方案

當(dāng)然這種方案不一定能保障百分百投遞成功踢关,但是基本上可以保障大概99.9%的消息是OK的伞鲫,有些特別極端的情況只能是人工去做補(bǔ)償了粘茄,或者使用定時(shí)任務(wù).

主要就是為了減少DB操作

方案流程圖
image
  • Upstream Service
    上游服務(wù),即生產(chǎn)端
  • Downstream service
    下游服務(wù),即消費(fèi)端
  • Callback service
    回調(diào)服務(wù)
方案實(shí)現(xiàn)流程
  • step1 一定要先將業(yè)務(wù)消息入庫(kù),然后Pro再發(fā)出消息,順序不能錯(cuò)!
  • step2 在發(fā)送消息之后,緊接著Pro再發(fā)送一條消息(Second Send Delay Check),即延遲消息投遞檢查,這里需要設(shè)置一個(gè)延遲時(shí)間,比如5分鐘之后進(jìn)行投遞.
  • step3 Con監(jiān)聽(tīng)指定的隊(duì)列,處理收到的消息.
  • step4 處理完成之后,發(fā)送一個(gè)confirm消息,也就是回送響應(yīng),但是其不是普通的ACK,而是重新生成一條消息,投遞到MQ,表示處理成功.
  • Callback service是一個(gè)單獨(dú)的服務(wù),它扮演MSG DB角色,它通過(guò)MQ監(jiān)聽(tīng)下游服務(wù)發(fā)送的confirm消息,如果監(jiān)聽(tīng)到confirm消息,那么就對(duì)其持久化到MSG DB.
  • step6 5分鐘之后延遲消息發(fā)送到MQ,然后Callback service還是去監(jiān)聽(tīng)延遲消息所對(duì)應(yīng)的隊(duì)列,收到Check消息后去檢查DB中是否存在消息,如果存在柒瓣,則不需要做任何處理儒搭,如果不存在或者消費(fèi)失敗了,那么Callback service就需要主動(dòng)發(fā)起RPC通信給上游服務(wù)芙贫,告訴它延遲檢查的這條消息我沒(méi)有找到搂鲫,你需要重新發(fā)送,生產(chǎn)端收到信息后就會(huì)重新查詢(xún)BIZ DB然后將消息發(fā)送出去.
設(shè)計(jì)目的

少做一次DB的存儲(chǔ),在高并發(fā)場(chǎng)景下,最關(guān)心的不是消息百分百投遞成功,而是一定要保證性能磺平,保證能抗得住這么大的并發(fā)量魂仍。所以能節(jié)省數(shù)據(jù)庫(kù)的操作就盡量節(jié)省,異步地進(jìn)行補(bǔ)償.

其實(shí)在主流程里面是沒(méi)有Callback service的拣挪,它屬于一個(gè)補(bǔ)償?shù)姆?wù)擦酌,整個(gè)核心鏈路就是生產(chǎn)端入庫(kù)業(yè)務(wù)消息,發(fā)送消息到MQ菠劝,消費(fèi)端監(jiān)聽(tīng)隊(duì)列赊舶,消費(fèi)消息。其他的步驟都是一個(gè)補(bǔ)償機(jī)制赶诊。

小結(jié)

這兩種方案都是可行的笼平,需要根據(jù)實(shí)際業(yè)務(wù)來(lái)進(jìn)行選擇,方案二也是互聯(lián)網(wǎng)大廠(chǎng)更為經(jīng)典和主流的解決方案.但是若對(duì)性能要求不是那么高,方案一要更簡(jiǎn)單.

3 冪等性

3.1 什么是冪等性

用戶(hù)對(duì)于同一操作發(fā)起的一次請(qǐng)求或者多次請(qǐng)求的結(jié)果是一致的

比如數(shù)據(jù)庫(kù)的樂(lè)觀(guān)鎖,在執(zhí)行更新操作前,先去數(shù)據(jù)庫(kù)查詢(xún)version,然后執(zhí)行更新語(yǔ)句,以version作為條件,如果執(zhí)行更新時(shí)有其他人先更新了這張表的數(shù)據(jù),那么這個(gè)條件就不生效了,也就不會(huì)執(zhí)行操作了,通過(guò)這種樂(lè)觀(guān)鎖的機(jī)制來(lái)保障冪等性.

3.2 Con - 冪等性

3.2.1 什么是Con - 冪等性

在業(yè)務(wù)高峰期最容易產(chǎn)生消息重復(fù)消費(fèi)問(wèn)題,當(dāng)Con消費(fèi)完消息時(shí),在給Pro返回ack時(shí)由于網(wǎng)絡(luò)中斷,導(dǎo)致Pro未收到確認(rèn)信息,該條消息就會(huì)重新發(fā)送并被Con消費(fèi),但實(shí)際上該消費(fèi)者已成功消費(fèi)了該條消息,這就造成了重復(fù)消費(fèi).

而Con - 冪等性,即消息不會(huì)被多次消費(fèi),即使我們收到了很多一樣的消息.

3.2.2 主流冪等性實(shí)現(xiàn)方案

3.2.2.1 唯一ID+指紋碼

核心:利用數(shù)據(jù)庫(kù)主鍵去重
  • 唯一ID:業(yè)務(wù)表的主鍵
  • 指紋碼:為了區(qū)別每次正常操作的碼,每次操作時(shí)生成指紋碼舔痪;可以用時(shí)間戳+業(yè)務(wù)編號(hào)或者標(biāo)志位(具體視業(yè)務(wù)場(chǎng)景而定)


    image
  • 優(yōu)勢(shì)
    實(shí)現(xiàn)簡(jiǎn)單
  • 弊端
    高并發(fā)下有數(shù)據(jù)庫(kù)寫(xiě)入的性能瓶頸
  • 解決方案
    根據(jù)ID進(jìn)行分庫(kù)分表算法路由
小結(jié)

首先我們需要根據(jù)消息生成一個(gè)全局唯一ID寓调,然后還需要加上一個(gè)指紋碼。這個(gè)指紋碼它并不一定是系統(tǒng)去生成的锄码,而是一些外部的規(guī)則或者內(nèi)部的業(yè)務(wù)規(guī)則去拼接捶牢,它的目的就是為了保障這次操作是絕對(duì)唯一的。

將ID + 指紋碼拼接好的值作為數(shù)據(jù)庫(kù)主鍵巍耗,就可以進(jìn)行去重了秋麸。即在消費(fèi)消息前呢,先去數(shù)據(jù)庫(kù)查詢(xún)這條消息的指紋碼標(biāo)識(shí)是否存在炬太,沒(méi)有就執(zhí)行insert操作灸蟆,如果有就代表已經(jīng)被消費(fèi)了,就不需要管了

3.2.2.2 利用Redis原子性

這里我們使用Redis實(shí)現(xiàn)冪等,還需要考慮如下問(wèn)題

  • 我們是否要進(jìn)行數(shù)據(jù)落庫(kù),如果落庫(kù),那么數(shù)據(jù)庫(kù)和緩存如何做到原子性?
    如果你想用事務(wù),放棄吧,Redis緩存事務(wù)和MySQL事務(wù)根本不是同一個(gè)事務(wù)
  • 如果不落庫(kù),那么都存儲(chǔ)到緩存中,定時(shí)同步的策略如何設(shè)置為好?

這里只提用Redis的原子性去解決MQ冪等性重復(fù)消費(fèi)的問(wèn)題

MQ的冪等性問(wèn)題 根本在于的是生產(chǎn)端未正常接收ACK亲族,可能是網(wǎng)絡(luò)抖動(dòng)炒考、網(wǎng)絡(luò)中斷導(dǎo)致

可能的方案

Con在消費(fèi)開(kāi)始時(shí)將 ID放入到Redis的BitMap中,Pro每次生產(chǎn)數(shù)據(jù)時(shí)霎迫,從Redis的BitMap對(duì)應(yīng)位置若不能取出ID斋枢,則生產(chǎn)消息發(fā)送,否則不進(jìn)行消息發(fā)送知给。

但是有人可能會(huì)說(shuō)瓤帚,萬(wàn)一Con描姚,ProRedis命令執(zhí)行失敗了怎么辦,雖然又出現(xiàn)重復(fù)消費(fèi)又出現(xiàn)Redis非正常執(zhí)行命令的可能性極低戈次,但是萬(wàn)一呢轩勘?

OK,我們可以在Redis命令執(zhí)行失敗時(shí)怯邪,將消息落庫(kù)绊寻,每日用定時(shí)器,對(duì)這種極特殊的消息進(jìn)行處理悬秉。

4 Confirm機(jī)制

4.1 什么是Confirm機(jī)制

  • 消息的確認(rèn)
    Pro投遞消息后,如果Broker收到消息,則會(huì)給Pro一個(gè)應(yīng)答
  • Pro接收應(yīng)答
    用來(lái)確定這條消息是否正常地發(fā)送到Broker,該法也是消息可靠性投遞的核心保障!

4.2 Confirm機(jī)制流程圖

image

Pro發(fā)送消息到Broker,Broker接收到消息后,產(chǎn)生回送響應(yīng)
Pro中有一個(gè)Confirm Listener異步監(jiān)聽(tīng)響應(yīng)應(yīng)答

4.2 實(shí)現(xiàn)Confirm機(jī)制

  1. 在channel上開(kāi)啟確認(rèn)模式:channel.confirmSelect()
  2. 在channel上添加監(jiān)聽(tīng):addConfirmListener
    監(jiān)聽(tīng)成功和失敗的返回結(jié)果澄步,根據(jù)具體的結(jié)果對(duì)消息進(jìn)行重新發(fā)送、或記錄日志等后續(xù)處理

接下來(lái)就讓我們根據(jù)原理進(jìn)行實(shí)操吧!

  • Con端


    image
  • Pro端


    image
  • 啟動(dòng)Con,檢查管控臺(tái)


    image

    image
  • 啟動(dòng)Pro


    image

    image

5 Return機(jī)制

5.1 什么是Return機(jī)制

  • Return Listener 用于處理一些不可路由的消息
  • Pro通過(guò)指定一個(gè)Exchange和Routingkey,把消息送到某一個(gè)隊(duì)列中,然后Con監(jiān)聽(tīng)隊(duì)列,進(jìn)行消費(fèi)
  • 但如果我們?cè)诎l(fā)送消息時(shí),當(dāng)前Exchange不存在或者Routingkey路由不到,如果我們要監(jiān)聽(tīng)這種不可達(dá)的消息,就要用到Return Listener

5.2 Return機(jī)制示意圖

image

5.3 實(shí)現(xiàn)Return機(jī)制

  • 添加return監(jiān)聽(tīng):addReturnListener和泌,生產(chǎn)端去監(jiān)聽(tīng)這些不可達(dá)的消息镐躲,做一些后續(xù)處理是趴,比如說(shuō)简僧,記錄下消息日志兰绣,或者及時(shí)去跟蹤記錄,有可能重新設(shè)置一下就好了

在基礎(chǔ)的API中的一個(gè)關(guān)鍵的配置項(xiàng):Mandatory

  • 如果為true聋丝,則監(jiān)聽(tīng)器會(huì)接收到路由不可達(dá)的消息,然后進(jìn)行后續(xù)處理

  • 如果為false,那么broker端自動(dòng)刪除該消息

  • Con


    image
  • Pro


    image
  • 啟動(dòng)Con


    image

    image

    image
  • 啟動(dòng)Pro
    由于Pro設(shè)置的是一個(gè)錯(cuò)誤的路由key索烹,所以消費(fèi)端沒(méi)有任何打印,而生產(chǎn)端打印了如下內(nèi)容


    image
  • 如果我們將 Mandatory 屬性設(shè)置為false弱睦,對(duì)于不可達(dá)的消息會(huì)被Broker直接刪除百姓,那么Pro就不會(huì)進(jìn)行任何打印了。如果我們的路由key設(shè)置為正確的况木,那么Con能夠正確消費(fèi)垒拢,Pro也不會(huì)進(jìn)行任何打印。

6 Con - 自定義監(jiān)聽(tīng)

  • 之前我們都是在代碼中編寫(xiě)while循環(huán),通過(guò)consumer.nextDelivery方法獲取下一條消息,然后進(jìn)行消費(fèi)處理
  • 其實(shí)我們還有另一種選擇,使用自定義的Consumer,它更方便,解耦性更強(qiáng),也是在實(shí)際工作中最常用的使用方式

自定義Con實(shí)現(xiàn)只需要繼承 DefaultConsumer 類(lèi)火惊,重寫(xiě) handleDelivery 方法即可!

6.1 代碼實(shí)現(xiàn)

  • 自定義Con


    image
  • Con


    image
  • Pro


    image
  • 啟動(dòng)Con后,查看管控臺(tái)


    image

    image
  • 啟動(dòng)Pro,Con接收消息


    image

7 Con - 限流

7.1 什么是Con - 限流

7.1.1 消息過(guò)載場(chǎng)景

  • 假設(shè)我們有這樣的場(chǎng)景
    Rabbitmq服務(wù)器有上萬(wàn)條未處理的消息,我們隨便打開(kāi)一個(gè)Con - Client,會(huì)造成:巨量的消息瞬間全部推送過(guò)來(lái),然而我們單個(gè)客戶(hù)端無(wú)法同時(shí)處理這么多數(shù)據(jù)!此時(shí)很有可能導(dǎo)致服務(wù)器崩潰求类,嚴(yán)重的可能導(dǎo)致線(xiàn)上的故障。
  • 還有一些其他的場(chǎng)景屹耐,比如說(shuō)單個(gè)Pro一分鐘產(chǎn)生了幾百條數(shù)據(jù),但是單個(gè)Con一分鐘可能只能處理60條,這個(gè)時(shí)候Pro-Con肯定是不平衡的尸疆。通常Pro是沒(méi)辦法做限制的。所以Con肯定需要做一些限流措施惶岭,否則如果超出最大負(fù)載寿弱,可能導(dǎo)致Con性能下降,服務(wù)器卡頓甚至崩潰等一系列嚴(yán)重后果

因此,我們需要限流

7.1.2 Con - 限流機(jī)制

RabbitMQ提供了一種qos (服務(wù)質(zhì)量保證)功能,即在非自動(dòng)確認(rèn)消息的前提下,如果一定數(shù)目的消息 (通過(guò)基于Con或者channel設(shè)置Qos的值) 未被確認(rèn)前,不消費(fèi)新的消息

不能設(shè)置自動(dòng)簽收功能(autoAck = false)
如果消息未被確認(rèn),就不會(huì)到達(dá)Con,目的就是給Pro減壓

限流設(shè)置API

void BasicQos(uint prefetchSize, ushort prefetchCount, bool global);

  • prefetchSize: 單條消息的大小限制按灶,Con通常設(shè)置為0症革,表示不做限制
  • prefetchCount: 一次最多能處理多少條消息
  • global: 是否將上面設(shè)置true應(yīng)用于channel級(jí)別還是取false代表Con級(jí)別

prefetchSize和global這兩項(xiàng),RabbitMQ沒(méi)有實(shí)現(xiàn),暫且不研究
prefetchCount在 autoAck=false 的情況下生效,即在自動(dòng)應(yīng)答的情況下該值無(wú)效

手工ACK
void basicAck(Integer deliveryTag,boolean multiple)
調(diào)用這個(gè)方法就會(huì)主動(dòng)回送給Broker一個(gè)應(yīng)答鸯旁,表示這條消息我處理完了噪矛,你可以給我下一條了量蕊。參數(shù)multiple表示是否批量簽收,由于我們是一次處理一條消息摩疑,所以設(shè)置為false

7.2 實(shí)現(xiàn)Con - 限流

  • 自定義Con


    image
  • Con


    image
  • Pro


    image
  • 啟動(dòng)Con,查看管控臺(tái)


    image
    image
  • 啟動(dòng)Pro,開(kāi)始發(fā)送消息,Con接收消息


    image
  • 實(shí)現(xiàn)限流,僅僅處理一條消息,其余的都在等待
    image
  • 現(xiàn)在,我們開(kāi)啟ACK應(yīng)答處理


    image
  • 重新啟動(dòng)Con,發(fā)現(xiàn)剩余的2條消息也全都發(fā)送并接收了!
  • 我們之前是注釋掉手工ACK方法,然后啟動(dòng)消費(fèi)端和生產(chǎn)端畏铆,當(dāng)時(shí)Con只打印一條消息,這是因?yàn)槲覀冊(cè)O(shè)置了手工簽收,并且設(shè)置了一次只處理一條消息,當(dāng)我們沒(méi)有回送ACK應(yīng)答時(shí)雷袋,Broker端就認(rèn)為Con還沒(méi)有處理完這條消息,基于這種限流機(jī)制就不會(huì)給Con發(fā)送新的消息了,所以Con那時(shí)只打印了一條消息


    image

    image

8 Con - ACK & 重回隊(duì)列機(jī)制

8.1 ACK & NACK

當(dāng)我們?cè)O(shè)置autoACK=false 時(shí),就可以使用手工ACK方式了,其實(shí)手工方式包括了手工ACK與NACK

當(dāng)我們手工 ACK 時(shí),會(huì)發(fā)送給Broker一個(gè)應(yīng)答,代表消息處理成功,Broker就可回送響應(yīng)給Pro.
NACK 則表示消息處理失敗,如果設(shè)置了重回隊(duì)列,Broker端就會(huì)將沒(méi)有成功處理的消息重新發(fā)送.

使用方式

  • Con消費(fèi)時(shí),如果由于業(yè)務(wù)異常,我們可以手工 NACK 記錄日志,然后進(jìn)行補(bǔ)償
API:void basicNack(long deliveryTag, boolean multiple, boolean requeue)
  • 如果由于服務(wù)器宕機(jī)等嚴(yán)重問(wèn)題,我們就需要手工 ACK 保障Con消費(fèi)成功
API:void basicAck(long deliveryTag, boolean multiple)

8.2 重回隊(duì)列

  • 重回隊(duì)列是為了對(duì)沒(méi)有處理成功的消息,將消息重新投遞給Broker
  • 重回隊(duì)列,會(huì)把消費(fèi)失敗的消息重新添加到隊(duì)列的尾端,供Con繼續(xù)消費(fèi)
  • 一般在實(shí)際應(yīng)用中,都會(huì)關(guān)閉重回隊(duì)列,即設(shè)置為false

8.3 實(shí)現(xiàn)機(jī)制

  • Con,關(guān)閉自動(dòng)簽收功能


    image
  • 自定義Con,對(duì)第一條消息(序號(hào)0)進(jìn)行NACK,并設(shè)置重回隊(duì)列


    image
  • Pro 對(duì)消息設(shè)置序號(hào),以便區(qū)分


    image
  • 啟動(dòng)Con,查看管控臺(tái)


    image

    image
  • 啟動(dòng)Pro,這里第一條消息由于我們調(diào)用了NACK辞居,并且設(shè)置了重回隊(duì)列楷怒,所以會(huì)導(dǎo)致該條消息一直重復(fù)發(fā)送,消費(fèi)端就會(huì)一直循環(huán)消費(fèi)


    image

    image

9 TTL機(jī)制

9.1 什么是TTL

  • TTL(Time To Live),即生存時(shí)間
  • RabbitMQ支持消息的過(guò)期時(shí)間瓦灶,在消息發(fā)送時(shí)可以進(jìn)行指定
  • RabbitMQ支持為每個(gè)隊(duì)列設(shè)置消息的超時(shí)時(shí)間鸠删,從消息入隊(duì)列開(kāi)始計(jì)算,只要超過(guò)了隊(duì)列的超時(shí)時(shí)間配置贼陶,那么消息會(huì)被自動(dòng)清除

9.2 管控臺(tái)演示

  • 新增一個(gè)Q


    image

    image
  • 新增一個(gè)交換機(jī)


    image

    image
  • 綁定


    image

    image
  • Q中也顯示了相關(guān)的綁定信息


    image
  • 發(fā)送消息


    image

    image
  • 10s后,消息被清除為0


    image

10 死信隊(duì)列機(jī)制

10.1 什么是死信隊(duì)列

DLX - 死信隊(duì)列(dead-letter-exchange)
利用DLX,當(dāng)消息在一個(gè)隊(duì)列中變成死信 (dead message) 之后,它能被重新publish到另一個(gè)Exchange中,這個(gè)Exchange就是DLX.

10.2 死信隊(duì)列的產(chǎn)生場(chǎng)景

  • 消息被拒絕(basic.reject / basic.nack),并且requeue = false
  • 消息因TTL過(guò)期
  • 隊(duì)列達(dá)到最大長(zhǎng)度

10.3 死信的處理過(guò)程

  • DLX亦為一個(gè)普通的Exchange,它能在任何隊(duì)列上被指定,實(shí)際上就是設(shè)置某個(gè)隊(duì)列的屬性
  • 當(dāng)某隊(duì)列中有死信時(shí),RabbitMQ會(huì)自動(dòng)地將該消息重新發(fā)布到設(shè)置的Exchange,進(jìn)而被路由到另一個(gè)隊(duì)列
  • 可以監(jiān)聽(tīng)這個(gè)隊(duì)列中的消息做相應(yīng)的處理.該特性可以彌補(bǔ)RabbitMQ 3.0以前支持的immediate參數(shù)的功能

10.4 死信隊(duì)列的配置

  • 設(shè)置死信隊(duì)列的exchange和queue,然后進(jìn)行綁定

    • Exchange:dlx.exchange
    • Queue: dlx.queue
    • RoutingKey:#
  • 正常聲明交換機(jī)刃泡、隊(duì)列、綁定碉怔,只不過(guò)我們需要在隊(duì)列加上一個(gè)參數(shù)即可

arguments.put(" x-dead-letter-exchange"烘贴,"dlx.exchange");

這樣消息在過(guò)期、requeue撮胧、 隊(duì)列在達(dá)到最大長(zhǎng)度時(shí)桨踪,消息就可以直接路由到死信隊(duì)列!

10.5 實(shí)操演示

  • 自定義Con


    image
  • Pro


    image
  • Con


    image
  • 啟動(dòng)Con,查看管控臺(tái)


    image

    image

    image

    image
  • 現(xiàn)在,讓我們停止Con,并啟動(dòng)Pro,由于沒(méi)有Con,TTL為10s的消息將送往死信隊(duì)列


    image
  • 10s后


    image
  • 實(shí)際環(huán)境我們還需要對(duì)死信隊(duì)列進(jìn)行一個(gè)監(jiān)聽(tīng)和處理芹啥,當(dāng)然具體的處理邏輯和業(yè)務(wù)相關(guān)锻离,這里只是簡(jiǎn)單演示死信隊(duì)列是否生效。

11 總結(jié)

本文專(zhuān)注RabbitMQ高級(jí)特性的學(xué)習(xí)

首先介紹了大廠(chǎng)在實(shí)際使用中是如何保障消息投遞成功和冪等性的墓怀,以及對(duì)RabbitMQ的確認(rèn)消息汽纠、返回消息、ACK與重回隊(duì)列傀履、消息的限流疏虫,以及對(duì)超時(shí)時(shí)間、死信隊(duì)列的使用

最后,感謝您的閱讀!

參考

RabbitMQ 100% 投遞成功方案詳解
RabbitMQ 從入門(mén)到精通(二)
RabbitMQ 冪等性概念及業(yè)界主流解決方案
RabbitMQ從入門(mén)到精通(三)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啤呼,一起剝皮案震驚了整個(gè)濱河市卧秘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌官扣,老刑警劉巖翅敌,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異惕蹄,居然都是意外死亡蚯涮,警方通過(guò)查閱死者的電腦和手機(jī)治专,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)遭顶,“玉大人张峰,你說(shuō)我怎么就攤上這事“羝欤” “怎么了喘批?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)铣揉。 經(jīng)常有香客問(wèn)我饶深,道長(zhǎng),這世上最難降的妖魔是什么逛拱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任敌厘,我火速辦了婚禮,結(jié)果婚禮上朽合,老公的妹妹穿的比我還像新娘俱两。我一直安慰自己,他們只是感情好曹步,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布锋华。 她就那樣靜靜地躺著,像睡著了一般箭窜。 火紅的嫁衣襯著肌膚如雪毯焕。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天磺樱,我揣著相機(jī)與錄音纳猫,去河邊找鬼。 笑死竹捉,一個(gè)胖子當(dāng)著我的面吹牛芜辕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播块差,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼侵续,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了憨闰?” 一聲冷哼從身側(cè)響起状蜗,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鹉动,沒(méi)想到半個(gè)月后轧坎,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡泽示,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年缸血,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜜氨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捎泻,死狀恐怖飒炎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笆豁,我是刑警寧澤郎汪,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站渔呵,受9級(jí)特大地震影響怒竿,放射性物質(zhì)發(fā)生泄漏砍鸠。R本人自食惡果不足惜扩氢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爷辱。 院中可真熱鬧录豺,春花似錦、人聲如沸饭弓。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)弟断。三九已至咏花,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間阀趴,已是汗流浹背昏翰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刘急,地道東北人棚菊。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叔汁,于是被迫代替她去往敵國(guó)和親统求。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容