RabbitMQ高級(jí)特性之死信隊(duì)列深入解析
死信隊(duì)列
死信(dead message) 簡(jiǎn)單理解就是因?yàn)榉N種原因, ?法被消費(fèi)的信息, 就是死信.
有死信, ?然就有死信隊(duì)列. 當(dāng)消息在?個(gè)隊(duì)列中變成死信之后,它能被重新被發(fā)送到另?個(gè)交換器
中,這個(gè)交換器就是DLX( Dead Letter Exchange ), 綁定DLX的隊(duì)列, 就稱為死信隊(duì)列(Dead
Letter Queue,簡(jiǎn)稱DLQ).

消息變成死信?般是由于以下?種情況:
1. 消息被拒絕( Basic.Reject/Basic.Nack ),并且設(shè)置 requeue 參數(shù)為 false.
2. 消息過期.
3. 隊(duì)列達(dá)到最??度.
添加配置
spring:
application:
name: rabbit-extensions-demo
rabbitmq:
addresses: amqp://study:study@47.98.109.138:5672/extension常量類
public class Constants {
//死信
public static final String NORMAL_QUEUE = "normal.queue";
public static final String NORMAL_EXCHANGE = "normal.exchange";
public static final String DL_QUEUE = "dl.queue";
public static final String DL_EXCHANGE= "dl.exchange";
}聲明隊(duì)列和交換機(jī)并綁定二者關(guān)系
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import rabbitextensionsdemo.constant.Constants;
@Configuration
public class DLConfig {
//正常的交換機(jī)和隊(duì)列
@Bean("normalQueue")
public Queue normalQueue(){
return QueueBuilder.durable(Constants.NORMAL_QUEUE)
.deadLetterExchange(Constants.DL_EXCHANGE)
.deadLetterRoutingKey("dlx")
.build();
}
@Bean("normalExchange")
public DirectExchange normalExchange(){
return ExchangeBuilder.directExchange(Constants.NORMAL_EXCHANGE).build();
}
@Bean("normalBinding")
public Binding normalBinding(@Qualifier("normalQueue") Queue queue, @Qualifier("normalExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("normal").noargs();
}
//死信交換機(jī)和隊(duì)列
@Bean("dlQueue")
public Queue dlQueue(){
return QueueBuilder.durable(Constants.DL_QUEUE).build();
}
@Bean("dlExchange")
public DirectExchange dlExchange(){
return ExchangeBuilder.directExchange(Constants.DL_EXCHANGE).build();
}
@Bean("dlBinding")
public Binding dlBinding(@Qualifier("dlQueue") Queue queue, @Qualifier("dlExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("dlx").noargs();
}
}死信--消息過期
給隊(duì)列設(shè)置TTL

編寫生產(chǎn)消息代碼
@RequestMapping("/dl")
public String dl() {
System.out.println("dl...");
//發(fā)送普通消息
rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test...");
System.out.printf("%tc 消息發(fā)送成功 \n", new Date());
return "消息發(fā)送成功";
}編寫消費(fèi)消息代碼
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import rabbitextensionsdemo.constant.Constants;
import java.util.Date;
@Component
public class DLListener {
@RabbitListener(queues = Constants.DL_QUEUE)
public void dlHandMessage(Message message, Channel channel) throws Exception {
//消費(fèi)者邏輯
System.out.printf("[dl.queue] %tc 接收到消息: %s, deliveryTag: %d \n", new Date(), new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag());
}
}觀察現(xiàn)象




我們可以看到,消息在10秒后過期,從normal隊(duì)列進(jìn)入到了死信隊(duì)列,消息進(jìn)入到死信隊(duì)列后被消費(fèi)。
死信--消息超過隊(duì)列最大長(zhǎng)度
設(shè)置隊(duì)列的最大長(zhǎng)度

編寫生產(chǎn)消息代碼
@RequestMapping("/dl")
public String dl() {
//測(cè)試隊(duì)列長(zhǎng)度
for (int i = 0; i < 20; i++) {
rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test..."+i);
}
return "消息發(fā)送成功";
}編寫消費(fèi)消息代碼
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import rabbitextensionsdemo.constant.Constants;
import java.util.Date;
@Component
public class DLListener {
@RabbitListener(queues = Constants.DL_QUEUE)
public void dlHandMessage(Message message, Channel channel) throws Exception {
//消費(fèi)者邏輯
System.out.printf("[dl.queue] %tc 接收到消息: %s, deliveryTag: %d \n", new Date(), new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag());
}
}觀察現(xiàn)象



此時(shí)我們可以看到,給隊(duì)列設(shè)置了最大長(zhǎng)度為10,但是隊(duì)列接收到了20條消息,就會(huì)導(dǎo)致前10條消息變成死信。
死信--消息被拒絕
編寫生產(chǎn)消息代碼
@RequestMapping("/dl")
public String dl() {
System.out.println("dl...");
//發(fā)送普通消息
rabbitTemplate.convertAndSend(Constants.NORMAL_EXCHANGE, "normal", "dl test...");
System.out.printf("%tc 消息發(fā)送成功 \n", new Date());
return "消息發(fā)送成功";
}編寫消費(fèi)消息代碼
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import rabbitextensionsdemo.constant.Constants;
@Component
public class DLListener {
@RabbitListener(queues = Constants.NORMAL_QUEUE)
public void handMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//消費(fèi)者邏輯
System.out.printf("[normal.queue]接收到消息: %s, deliveryTag: %d \n", new String(message.getBody(),"UTF-8"), message.getMessageProperties().getDeliveryTag());
//進(jìn)行業(yè)務(wù)邏輯處理
System.out.println("業(yè)務(wù)邏輯處理");
int num = 3/0;
System.out.println("業(yè)務(wù)處理完成");
//肯定確認(rèn)
channel.basicAck(deliveryTag,false);
} catch (Exception e) {
//否定確認(rèn)
channel.basicNack(deliveryTag, false, false); //requeue為false, 該消息成為死信
}
}
}觀察現(xiàn)象



可以看到,normal隊(duì)列中的消息在被消費(fèi)時(shí)因?yàn)榘l(fā)生了異常而執(zhí)行到了拒絕消息的代碼,而且設(shè)置了消息不重新入隊(duì),導(dǎo)致消息變成了死信,進(jìn)而進(jìn)入到了死信隊(duì)列。
面試題
1.死信隊(duì)列的概念
死信(Dead Letter)是消息隊(duì)列中的?種特殊消息, 它指的是那些?法被正常消費(fèi)或處理的消息. 在消息隊(duì)列系統(tǒng)中, 如RabbitMQ, 死信隊(duì)列?于存儲(chǔ)這些死信消息。
2.死信的來源
1) 消息過期: 消息在隊(duì)列中存活的時(shí)間超過了設(shè)定的TTL
2) 消息被拒絕: 消費(fèi)者在處理消息時(shí), 可能因?yàn)橄?nèi)容錯(cuò)誤, 處理邏輯異常等原因拒絕處理該消息. 如果拒絕時(shí)指定不重新?隊(duì)(requeue=false), 消息也會(huì)成為死信.
3) 隊(duì)列滿了: 當(dāng)隊(duì)列達(dá)到最??度, ?法再容納新的消息時(shí), 新來的消息會(huì)被處理為死信.
3.死信的應(yīng)用場(chǎng)景
對(duì)于RabbitMQ來說, 死信隊(duì)列是?個(gè)?常有?的特性. 它可以處理異常情況下,消息不能夠被消費(fèi)者正確消費(fèi)?被置?死信隊(duì)列中的情況, 應(yīng)?程序可以通過消費(fèi)這個(gè)死信隊(duì)列中的內(nèi)容來分析當(dāng)時(shí)所遇到的異常情況, 進(jìn)?可以改善和優(yōu)化系統(tǒng).
?如: ???付訂單之后, ?付系統(tǒng)會(huì)給訂單系統(tǒng)返回當(dāng)前訂單的?付狀態(tài)
為了保證?付信息不丟失, 需要使?到死信隊(duì)列機(jī)制. 當(dāng)消息消費(fèi)異常時(shí), 將消息投?到死信隊(duì)列中, 由訂單系統(tǒng)的其他消費(fèi)者來監(jiān)聽這個(gè)隊(duì)列, 并對(duì)數(shù)據(jù)進(jìn)?處理(?如發(fā)送?單等,進(jìn)???確認(rèn)).
場(chǎng)景的應(yīng)?場(chǎng)景還有:
• 消息重試:將死信消息重新發(fā)送到原隊(duì)列或另?個(gè)隊(duì)列進(jìn)?重試處理.
• 消息丟棄:直接丟棄這些?法處理的消息,以避免它們占?系統(tǒng)資源.
• ?志收集:將死信消息作為?志收集起來,?于后續(xù)分析和問題定位.
到此這篇關(guān)于詳解RabbitMQ高級(jí)特性之死信隊(duì)列的文章就介紹到這了,更多相關(guān)RabbitMQ死信隊(duì)列內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中List數(shù)組用逗號(hào)分隔開轉(zhuǎn)成字符串2種方法
在我們?nèi)粘i_發(fā)中,在前后端交互的時(shí)候會(huì)遇到多個(gè)id或其他字段存放到一個(gè)字段中,這時(shí)我們會(huì)遇到一個(gè)List(集合)---->String(單個(gè)字段),這篇文章主要給大家介紹了關(guān)于java中List數(shù)組用逗號(hào)分隔開轉(zhuǎn)成字符串的2種方法,需要的朋友可以參考下2023-10-10
Java 批量獲取地址間距離工具(支持中轉(zhuǎn)站)
本文主要介紹了Java批量獲取地址間距離,獲取兩個(gè)地址間距離,實(shí)現(xiàn)方式比較多,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
spring boot使用logback日志級(jí)別打印控制操作
這篇文章主要介紹了spring boot使用logback日志級(jí)別打印控制操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-03-03
Java中ResultSetMetaData 元數(shù)據(jù)的具體使用
本文主要介紹了Java中ResultSetMetaData 元數(shù)據(jù)的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04
Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索
這篇文章主要為大家介紹了Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
Java實(shí)現(xiàn)合并兩個(gè)有序序列算法示例
這篇文章主要介紹了Java實(shí)現(xiàn)合并兩個(gè)有序序列算法,簡(jiǎn)單描述了序列合并算法的原理與java合并有序序列的具體操作步驟及相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09
Java中Lambda表達(dá)式和函數(shù)式接口的使用和特性
Java Lambda表達(dá)式是一種函數(shù)式編程的特性,可簡(jiǎn)化匿名內(nèi)部類的寫法,與函數(shù)式接口搭配使用,實(shí)現(xiàn)代碼簡(jiǎn)潔、可讀性高、易于維護(hù)的特點(diǎn),適用于集合操作、多線程編程等場(chǎng)景2023-04-04

