RabbitMQ 死信隊列基礎概念與配置概述
1. 前言
Hello,大家好。本小節會為同學們介紹 RabbitMQ 中的死信隊列及其基礎配置。死信隊列作為 RabbitMQ 中最后一個特性,其在實際工作中發揮著重要的作用。本節會從死信隊列的前置概念開始,到死信隊列的基礎概念,最后介紹死信隊列的基本使用方法和基本配置結束,詳細介紹死信隊列的基礎概念和基本使用方法。
話不多說,讓我們直入正題吧。
本節主要內容:
-
死信隊列前置概念概述;
-
死信隊列基礎概念概述;
-
死信隊列基本使用概述;
2. 死信隊列前置概念概述
在正式介紹死信隊列的基礎概念之前,需要同學們先了解一些死信隊列的前置概念,這些前置概念是后續理解死信隊列的基礎,同學們只有對這些前置概念有一個理解之后,才能很好地理解什么是死信隊列。
什么是隊列:
隊列并不是只存在于 RabbitMQ 中,隊列是一種計算機領域中的基本數據結構,其描述了數據在計算機內存中垂直分布的特點。 我們可以把隊列看做是我們日常生活中排隊做核酸的場景:當有一個人去做的時候,這個時候沒有隊伍,直接做就行了;當有十個人去做的時候,這個時候就需要排隊了,而排隊的這個過程就是隊列形成的過程。
當再有人需要去做的時候,這個人只能排在隊尾,不能夠插隊,而隊前的人由于比隊尾的人先到,所以隊前的人就比隊尾的人先做核酸,以此反復這個過程,直到所有人都做完核酸為止。
而隊列描述的就是這樣的場景,在上述例子中,形成隊伍的過程就是我們的應用數據入隊的過程,先入隊的數據可以先被處理,而后入隊的數據只能等待前入隊的數據處理完畢之后才能進行處理,這就是隊列先入先出的特點。
假設我們有 6 條數據需要入隊,以下是數據入隊過程:

1 號數據會先入隊,然后排在 1 好數據后面的 2 3 4 5 6 好數據會依次入隊,入隊完成后的隊列如下圖所示:

說白了,隊列就是數據按照固定的排列方式在計算機中存儲的一種表現形式,而隊列中的數據處理原則就是先入隊的數據先進行處理,后入隊的數據后進行處理。RabbitMQ 中的隊列也是一樣的,只不過這些隊列里面存儲的都是被稱為消息的數據,所以這些隊列被稱為消息隊列, 即在 RabbitMQ 中,根據消息類型的不同,會形成很多不同類型的隊列,但是這些隊列歸根到底,其本質依然是消息隊列。
什么是死信:
我們知道,在 RabbitMQ 中充當主角的就是消息,在不同場景下,消息會有不同地表現。死信就是消息在特定場景下的一種表現形式,這些場景包括:消息被拒絕訪問,即 RabbitMQ Server 返回 nack 的信號時、消息的 TTL 過期時、消息隊列達到最大長度,消息不能入隊時。
經常產生死信的場景就是上述三種場景,即消息在這三種場景中時,被稱為死信。
3. 死信隊列基礎概念概述
通過前置概念的介紹,我們知道了死信的基礎的概念,那么死信隊列又是什么呢?
結合上述對隊列基礎概念的介紹,我們不難得出:死信隊列就是用于儲存死信的消息隊列,在死信隊列中,有且只有死信構成,不會存在其余類型的消息,這就是死信隊列。
死信隊列在 RabbitMQ 中并不會單獨存在,往往死信隊列都會綁定這一個普通的消息隊列,當所綁定的消息隊列中,有消息變成死信了,那么這個消息就會重新被交換機路由到指定的死信隊列中去,我們可以通過對這個死信隊列進行監聽,從而手動的去對這一消息進行補償。
那么,我們到底如何來使用死信隊列呢?
4. 死信隊列基本使用概述
在 RabbitMQ 中,死信隊列的標識為 x-dead-letter-exchange ,通過觀察死信隊列的標識,我們不難發現,其標識最后為 exchange ,即 RabbitMQ 中的交換機,沒錯,RabbitMQ 中的死信隊列就是由死信交換機而得出的。
要想使用死信隊列,我們需要首先聲明一個普通的消息隊列,并將死信隊列的標識綁定到這個普通的消息隊列上, 這個過程需要我們在生產端進行配置,代碼如下所示:
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("xx");
connectionFactory.setPort("5672");
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChanel();
Map<String, Object> argumentsMap = new HashMap();
argumentsMap.put("x-dead-letter-exchange", "dlx_exchange");
channel.exchangeDeclare("dlx_common_exchange", "direct", true, false, null);
channel.queueDeclare("dlx_common_queue", true, false, false, argumentsMap);
channel.queueBind("dlx_common_queue", "dlx_common_exchange", routingKey);
代碼解釋:
第 1-5 行,我們使用 ConnectionFactory 創建了一個客戶端連接 RabbitMQ Server 的連接。
第 6 行,我們使用建立好的連接,來創建了一個頻道 channel 。
第 7-8 行,我們聲明了一個普通隊列的額外參數的 Map ,這個 Map 的 key 就是死信隊列的標識,value 就是我們后續聲明的真正的死信交換機的名稱。
第 9-10 行,我們依次使用 channel 的 exchangeDeclare 方法和 queueDeclare 方法,分別聲明了一個名為 dlx_common_exchange 的交換機和名為 dlx_common_queue 的普通消息隊列,之所以名稱中有 common ,是因為要對這個交換機和隊列做一個標識,表示該交換機和隊列是綁定了死信隊列的。
第 11 行,我們使用 channel 的 queueBind 方法來講聲明的普通交換機和消息隊列進行綁定,并且制定了 routingKey ,這樣消息就可以經 dlx_common_exchange 根據 routingKey 來路由到 dlx_common_queue 中。
在我們聲明了要綁定死信隊列的普通隊列之后,最后我們需要聲明真正的死信隊列,代碼如下所示:
// 省略客戶端連接 RabbitMQ Server 的過程
channel.exchangeDeclare("dlx_exchange", "direct", true, false, null);
channel.queueDeclare("dlx_queue", true, false, false, null);
channel.queueBind("dlx_queue", "dlx_exchange", routingKey);
代碼解釋:
第 1 行,我們使用 chanel 的 exchangeDeclare 方法來聲明了一個名為 dlx_exchange 的交換機。
第 2 行,我們使用 channel 的 queueDeclare 方法來聲明了一個名為 dlx_queue 的隊列。
第 3 行,我們使用 channel 的 queueBind 方法,來將 dlx_exchange 的交換機與 dlx_queue 隊列進行了綁定。
當我們完成上述過程之后,死信隊列就配置完成了,這也是死信隊列的基本使用方法。
Tips: 1. 從聲明死信隊列的代碼段中,我們不難看出,我們所聲明的交換機和隊列也都是普通的,只不過我們聲明的這個交換機和隊列是用來存儲 dlx_common_queue 隊列中的死信的;
2. 死信隊列的使用在實際工作中非常重要,它可以幫助我們對那些異常的消息進行監控,并根據這些監控信息制定相應的消息補償策略,這點同學們注意;
3. 一定要注意在聲明普通隊列時,我們聲明的名為 argumetsMap 的變量,這個是綁定死信隊列的關鍵。
5. 小結

本小節為同學們介紹了 RabbitMQ 中,死信隊列的前置概念、死信隊列的基礎概念,以及死信隊列的基本使用。我們只有在了解了死信隊列的前置概念之后,我們才能理解死信隊列的基礎概念,同時,我們只有清楚的明白了應用死信隊列的步驟,我們才能正確的用好死信隊列。
死信隊列在實際工作中使用頻率非常高,希望同學們可以清楚地理解本節中的基礎概念和代碼實現,這些都是應用死信隊列基礎中的基礎,望同學們注意。