自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

解決消息延遲和堆積問(wèn)題

開(kāi)發(fā) 前端
我們?cè)谏掀呀?jīng)說(shuō)明了如何解決消息丟失的問(wèn)題,也就是保證了消息的可靠性,那么其余兩個(gè)問(wèn)題同樣重要,這篇我們將講述其余兩個(gè)問(wèn)題的解決方式~!
文末本文轉(zhuǎn)載自微信公眾號(hào)「小菜良記」,作者蔡不菜丶。轉(zhuǎn)載本文請(qǐng)聯(lián)系小菜良記公眾號(hào)。
  • 消息可靠性問(wèn)題:如何確保發(fā)送的消息至少被消費(fèi)一次?
  • 延遲消息問(wèn)題:如何實(shí)現(xiàn)消息的延遲投遞?
  • 消息堆積問(wèn)題:如何解決數(shù)百萬(wàn)級(jí)以上消息堆積,無(wú)法及時(shí)消費(fèi)問(wèn)題?

我們?cè)谏掀呀?jīng)說(shuō)明了如何解決消息丟失的問(wèn)題,也就是保證了消息的可靠性,那么其余兩個(gè)問(wèn)題同樣重要,這篇我們將講述其余兩個(gè)問(wèn)題的解決方式~!

一、延遲消息

延遲消息 字面意思就是讓延遲接收消息,那么如何能讓消息延遲到達(dá)?這就是我們要思考解決的問(wèn)題,在了解延遲隊(duì)列之前我們需要先明白 RabbitMQ 中的兩個(gè)概念

  • 死信交換機(jī)
  • TTL

1)死信交換機(jī)

死信(dead letter),也就是廢棄已死亡的消息,那什么情況下一個(gè)普通的消息能夠成為死信?需要符合以下三個(gè)條件:

消費(fèi)者使用 basic.reject 或 basic.nack 聲明消費(fèi)失敗,并將消息的 requeue 參數(shù)設(shè)置為 false

消息是一個(gè)過(guò)期消息,超時(shí)后無(wú)人消費(fèi)

要投遞的隊(duì)列消息堆積滿(mǎn)了,最早的消息就會(huì)成為死信

而 死信交換機(jī) 便是 死信 的歸屬。

如果一個(gè)隊(duì)列配置了 dead-letter-exchange 屬性,指定了一個(gè)交換機(jī),那么隊(duì)列中的死信就會(huì)投遞到這個(gè)交換機(jī)中,而這個(gè)交換機(jī)就稱(chēng)為 死信交換機(jī) - DLX(Dead Letter Exchange )

步驟:當(dāng)生產(chǎn)者正常投遞到隊(duì)列(simple.queue)中,如果消費(fèi)者從隊(duì)列(simple.queue) 消費(fèi)消息卻聲明了 reject,那并且隊(duì)列綁定了死信交換機(jī)(dl.queue),那么這個(gè)時(shí)候成為死信的消息就會(huì)投遞到這個(gè)死信隊(duì)列(dl.queue)中。

死信投遞過(guò)程

從正常隊(duì)列 --> 死信隊(duì)列 的過(guò)程,我們必須聲明兩個(gè)關(guān)鍵信息

  • 死信交換機(jī)的名稱(chēng)
  • 死信交換機(jī)與死信隊(duì)列綁定的路由key

而這兩個(gè)信息也是我們投遞消息的基礎(chǔ)配置。

接下來(lái)我們簡(jiǎn)單模擬一下 條件1 所產(chǎn)生的場(chǎng)景

1、首先聲明一個(gè)死信交換機(jī)和死信隊(duì)列

我們這邊是使用簡(jiǎn)單的注解方式直接生成

生成死信交換機(jī)和死信隊(duì)列

通過(guò) RabbitMQ 控制臺(tái)界面可以看出已經(jīng)成功生成

2、聲明正常使用交換機(jī)與隊(duì)列

然后這個(gè)時(shí)候我們就可以創(chuàng)建一個(gè)正常使用的交換機(jī)與隊(duì)列,并指明死信交換機(jī)

同樣可以通過(guò)控制臺(tái)查看創(chuàng)建狀態(tài)

其中是否有聲明死信交換機(jī)我們可以通過(guò)隊(duì)列的 DLX 和 DLK 標(biāo)志判斷

3、模擬拒收

然后我們現(xiàn)在通過(guò)代碼模擬客戶(hù)端拒絕消息的場(chǎng)景

1)消息發(fā)送

2)消息接收

查看控制臺(tái),結(jié)果如下:

  1. 2021-11-06 23:56:52.095  INFO 2112 --- [ntContainer#0-1] c.l.m.c.listener.SpringRabbitListener    : 正常業(yè)務(wù)交換機(jī) | 接收到的消息 : [hello] 
  2. 2021-11-06 23:56:52.118  INFO 2112 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交換機(jī) | 接收到的消息 : hello 

這說(shuō)明我們死信交換機(jī)已經(jīng)成功發(fā)揮作用

2)TTL

以上我們已經(jīng)成功認(rèn)識(shí)到了 死信交換機(jī) 的使用,但是這與我們一開(kāi)始說(shuō)的 延遲隊(duì)列 似乎并沒(méi)有太大關(guān)系,莫急~接下來(lái)說(shuō)到的 TTL(Time-To-Live) 就是用來(lái)處理延遲消息的~!

在 TTL 的概念中,如果一個(gè)隊(duì)列中的消息 TTL 結(jié)束后仍未被消費(fèi),那么這個(gè)消息就會(huì)自動(dòng)變?yōu)樗佬?,?TTL 超時(shí)情況分為兩種:

  • 消息所在的隊(duì)列設(shè)置了存活時(shí)間
  • 消息本身設(shè)置了存活時(shí)間

我們同樣進(jìn)行上述 條件2 的模擬場(chǎng)景

  • 1、聲明死信交換機(jī)與死信隊(duì)列(上述已完成)
  • 2、聲明延遲隊(duì)列并指定死信交換機(jī)

同樣控制臺(tái)查看創(chuàng)建結(jié)果,并且我們發(fā)現(xiàn)不止有 DLX 和 DLK 標(biāo)志,還多了個(gè) TTL ,說(shuō)明該隊(duì)列是延遲隊(duì)列

  • 3、模擬消費(fèi)超時(shí)情況

我們往延遲隊(duì)列中發(fā)送一條消息,并且沒(méi)有消費(fèi)者進(jìn)行消費(fèi),等待 1 分鐘后查看是否能進(jìn)入 死信隊(duì)列 中

我們已經(jīng)發(fā)送了一條消息到延遲隊(duì)列并且一分鐘后也成功在控制臺(tái)發(fā)現(xiàn)了這條信息已經(jīng)進(jìn)入到了死信交換機(jī)

  1. 2021-11-07 00:01:30.854  INFO 32752 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交換機(jī) | 接收到的消息 : test ttl-message 

以上是配置了隊(duì)列超時(shí)時(shí)間,消息本身自然也能配置超時(shí)時(shí)間,當(dāng) 消息 和 隊(duì)列 都存在超時(shí)時(shí)間時(shí),那么就以最短的 TTL 為準(zhǔn),消息的超時(shí)配置如下:

如上圖所示,我們可以利用 Message 這個(gè)類(lèi)來(lái)傳遞消息信息,并設(shè)置上超時(shí)時(shí)間,我們?cè)O(shè)置的是 5000 ms,等待發(fā)送成功后,控制臺(tái)過(guò)5000 ms 也成功打印了死信交換機(jī)消費(fèi)的消息:

  1. 2021-11-07 00:03:09.048  INFO 39996 --- [ntContainer#1-1] c.l.m.c.listener.SpringRabbitListener    : 死信交換機(jī) | 接收到的消息 : this is a ttl message 

3)延遲隊(duì)列

我們上述是使用 死信交換機(jī) 來(lái)間接實(shí)現(xiàn) 延遲隊(duì)列 的效果,但實(shí)際在 RabbitMQ 不必如此麻煩,RabbitMQ 已經(jīng)為我們封裝好了插件,我們只需要下載安裝即可~

RabbitMQ 插件下載地址

我們進(jìn)入地址可以發(fā)現(xiàn)有許多插件,搜索 delay 關(guān)鍵字找到我們需要的插件進(jìn)行下載

下載完后直接上傳到 RabbitMQ 的插件目錄 - plugins,小菜這邊是使用 docker 臨時(shí)安裝測(cè)試的,所以已經(jīng)將該插件目錄掛載出來(lái)了:

  1. docker run -itd --name rabbitmq -v plugins:/plugins -p 15672:15672 -p 5672:5672 rabbitmq:management 

因此我這邊直接將插件上傳到容器中的 plugins 目錄即可~

然后進(jìn)入到容器中執(zhí)行以下命令進(jìn)行插件開(kāi)啟

  1. rabbitmq-plugins enable rabbitmq_delayed_message_exchange 

并且我們?cè)诳刂婆_(tái)創(chuàng)建交換機(jī)的時(shí)候可以看到 type 類(lèi)型多了個(gè)選項(xiàng)

成功執(zhí)行到這步就說(shuō)明已經(jīng)開(kāi)啟了 RabbitMQ 的延遲隊(duì)列功能

那接下來(lái)我們就可以來(lái)使用 DelayExchange,首先我們需要了解代碼的方式創(chuàng)建延遲交換機(jī):

方式1

方式2

當(dāng)我們?nèi)f事具備之后就可以來(lái)發(fā)送消息了

在發(fā)送消息的時(shí)候,消息頭中一定要攜帶上 x-delay 參數(shù),指定上延遲時(shí)間

通過(guò)這樣配置之后,我們可以在控制臺(tái)看到,經(jīng)過(guò)10秒后 delay.queue 才收到對(duì)應(yīng)消息,然后被對(duì)應(yīng)消費(fèi)者消費(fèi)

3)總結(jié)

我們上面從 死信交換機(jī) 到 TTL 到 延遲隊(duì)列,一步步認(rèn)識(shí)了如何實(shí)現(xiàn)延遲消息的功能,然后我們進(jìn)行一個(gè)小小的總結(jié):

問(wèn)題1:什么樣的消息會(huì)成為死信?

消息被消費(fèi)者 reject 或返回 nack

消息超時(shí)未及時(shí)消費(fèi)

消息隊(duì)列滿(mǎn)了

問(wèn)題2:消息超時(shí)的方式

給隊(duì)列設(shè)置 TTL 屬性

給消息設(shè)置 TTL 屬性

問(wèn)題3:如何使用延遲隊(duì)列

下載并啟用 RabbitMQ 延遲隊(duì)列插件

聲明一個(gè)交換機(jī),并將 delayed 屬性設(shè)置為 true

發(fā)送消息時(shí),添加 x-delay 頭,值為超時(shí)時(shí)間

問(wèn)題4:延遲隊(duì)列的使用場(chǎng)景

延遲發(fā)送短信通知

訂單自動(dòng)取消

庫(kù)存自動(dòng)回滾

二、惰性隊(duì)列

講完延遲隊(duì)列,我們繼續(xù)來(lái)認(rèn)識(shí)惰性隊(duì)列

講惰性隊(duì)列之前,我們先拋出一個(gè)問(wèn)題~

RabbitMQ 如何解決消息堆積問(wèn)題

什么情況下會(huì)出現(xiàn)消息堆積問(wèn)題?

  1. 當(dāng)生產(chǎn)者生產(chǎn)速度遠(yuǎn)遠(yuǎn)消費(fèi)者消費(fèi)速度
  2. 當(dāng)消費(fèi)者宕機(jī)沒(méi)有及時(shí)重啟

那么如何解決這個(gè)問(wèn)題?通常思路如下:

  1. 在消費(fèi)者機(jī)器重啟后,增加更多的消費(fèi)者進(jìn)行處理
  2. 在消費(fèi)者處理邏輯內(nèi)部開(kāi)辟線(xiàn)程池,利用多線(xiàn)程的方式提高處理速度
  3. 擴(kuò)大隊(duì)列的容量,提高堆積上限

這幾個(gè)方式從理論上來(lái)說(shuō)解決消息堆積問(wèn)題也是沒(méi)有問(wèn)題的,但是處理方式不夠優(yōu)雅甚至不夠靈活~ 那么除了以上的幾種解決方式,我們可以利用 RabbitMQ 中自帶的一種隊(duì)列類(lèi)型 -- 惰性隊(duì)列

什么是惰性隊(duì)列?我們認(rèn)識(shí)一下惰性隊(duì)列的幾個(gè)特性:

  • 接收到消息后直接存入磁盤(pán)而非內(nèi)存
  • 消費(fèi)者要消費(fèi)消息時(shí)才會(huì)從磁盤(pán)中讀取并加載到內(nèi)存中
  • 它支持百萬(wàn)級(jí)消息的存儲(chǔ)

說(shuō)到底,就是利用磁盤(pán)的緩沖機(jī)制,而這種機(jī)制的缺點(diǎn)就是消息的時(shí)效性會(huì)降低,性能受限于磁盤(pán)的IO,認(rèn)識(shí)特性和缺點(diǎn)之后,我們便來(lái)看看如何創(chuàng)建惰性隊(duì)列

方式1

方式2

方式3

該方式是直接基于命令行修改將一個(gè)正在運(yùn)行中的隊(duì)列修改為惰性隊(duì)列

  1. rabbitmqctl set_policy Lazy "^lazy-queue$" '{"queue-mode":"lazy"}' --apply-to queues   

其中幾個(gè)命令參數(shù)含義如下:

  • rabbitmqctl:命令行工具
  • set_policy:添加一個(gè)策略
  • Lazy:策略名稱(chēng),可以自定義
  • ^lazy-queue$:用正則表達(dá)式匹配隊(duì)列的名稱(chēng)
  • '{"queue-mode":"lazy"}':設(shè)置隊(duì)列為 lazy 模式
  • --apply-to queues:策略的作用對(duì)象,是所有的隊(duì)列

這種惰性隊(duì)列的方式盡管缺點(diǎn)是消息時(shí)效性會(huì)降低,但是在某些場(chǎng)景下也不是不能接受,何況它的優(yōu)點(diǎn)同樣明顯:

  1. 基于磁盤(pán)存儲(chǔ),消息上限高
  2. 沒(méi)有間歇性的 page-out,性能穩(wěn)定

 

到這里,我們就已經(jīng)講述了 RabbitMQ 的常見(jiàn)問(wèn)題,對(duì)于我們來(lái)說(shuō),普通的開(kāi)發(fā)場(chǎng)景可能比較少遇到這些問(wèn)題,但是沒(méi)遇到不等于沒(méi)有,所以我們還是需要多認(rèn)識(shí)來(lái)防患于未然!

 

責(zé)任編輯:武曉燕 來(lái)源: 小菜良記
相關(guān)推薦

2021-10-26 08:22:38

消息堆積擴(kuò)容RocketMQ

2024-07-29 00:01:00

RabbitMQ消息堆積

2023-12-21 08:01:41

RocketMQ消息堆積

2021-11-23 09:00:59

消息堆積擴(kuò)容RocketMQ

2024-06-24 08:42:11

2024-04-30 08:09:10

PulsarArthas消息隊(duì)列

2020-08-27 19:25:01

邊緣計(jì)算云計(jì)算安全

2022-10-31 09:30:32

kafkaconsumer服務(wù)端

2020-06-08 15:01:55

數(shù)據(jù)中心網(wǎng)絡(luò)架構(gòu)帶寬

2020-11-13 16:40:05

RocketMQ延遲消息架構(gòu)

2022-11-08 07:36:17

RocketMQ消費(fèi)者消息堆積

2012-01-12 12:08:02

Java

2023-12-26 18:22:05

RocketMQ延遲消息

2024-04-23 08:46:45

消息積壓KafkaMQ

2011-09-08 16:34:28

Windows7網(wǎng)絡(luò)延遲

2009-09-02 17:36:12

郵件服務(wù)器

2024-06-05 06:37:19

2024-12-09 08:44:58

2017-03-16 08:46:57

延時(shí)消息環(huán)形隊(duì)列數(shù)據(jù)結(jié)構(gòu)

2021-01-22 15:18:12

消息延遲閑魚(yú)長(zhǎng)連接
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)