参考:<<RabbitMQ实战指南>>
Dead-Letter-Exchange,简称DLX,可称为死信交换器、死信邮箱等等。
当消息在一个队列中变成死信(dead message)之后,它能被重新发送到另一个交换器中,这个交换器就是DLX,绑定DLX的队列就称之为死信队列。
消息变成死信的几种情况:
- 消息被拒绝(Basic.Reject/Basic.Nack),并且设置requeue参数为false
- 消息过期
- 队列到达最大长度
注:
- DLX也是一个正常的交换器,和一般的交换器没有区别,它能在任何队列上被指定,实际上就是设置某个队列的属性。
- 当这个队列中存在死信时,RabbitMQ就会自动地将这个消息重新发布到设置的DLX上去,进而被路由到另一个队列,即死信队列。可以监听这个队列中的消息、以进行相应的处理。
- 通过在channel.QueueDeclare方法中设置x-dead-letter-exchange参数来为这个队列添加DLX。
示例:
1. 原生api
声明了两个交换器 dlxExchangeName和normalExchangeName,分别绑定了两个队列dlxQueue和normalQueue。发送消息流程:
- 生产者首先发送一条携带路由键为normalRoutingKey的消息
- 经过交换器normalExchangeName将消息存储到normalQueue中
- 队列设置了消息的过期时间为5s,在这5s内没有消费者消费这条消息,那么这条消息就被判定为过期
- 由于设置了DLX,消息过期之后就会被转发到dlxExchangeName中,这时消息被存储到绑定的dlxQueue中,也就是这个死信队列中
...
// 死信交换器、队列
channel.exchangeDeclare(dlxExchangeName, "direct", true);
channel.queueDeclare(dlxQueueName, true, false, false, null);
channel.queueBind(dlxQueue, dlxExchangeName, dlxRoutingKey);
// 普通交换器、队列
channel.exchangeDeclare(normalExchangeName, "direct", true);
Map<String, Object> arguments = new HashMap<>(4);
arguments.put("x-message-ttl", 5000);// 设置消息过期时间
arguments.put("x-dead-letter-exchange", dlxExchangeName);// 为普通队列添加DLX
arguments.put("x-dead-letter-routing-key", dlxRoutingKey);// 为这个DLX指定路由键,不指定则使用原队列的路由键
channel.queueDeclare(queueName, true, false, false, arguments);
channel.queueBind(normalQueue, normalExchangeName, normalRoutingKey);
byte[] content = "Test Msg".getBytes("UTF-8");
channel.basicPublish(normalExchangeName, normalRoutingKey, false, null, content);
...
进入Web管理页面,创建的队列如下图所示:
- D:durable的缩写,设置了队列的持久化
- TTL:设置了过期时间
- DLX:这个队列设置了死信交换器
- DLK:指的是
x-dead-letter-routing-key
这个属性
2. springboot
(1)声明交换器、队列
和原生api一样,在声明队列时指定DLX,通过设置消息过期时间模拟。
@Bean
public DirectExchange dlxNormalExchange() {
return new DirectExchange(normalExchangeName);
}
@Bean
public Queue dlxNormalQueue() {
Map<String, Object> args = new HashMap<>(4);
// 设置消息过期时间
args.put("x-message-ttl", 5000);
// 为普通队列添加DLX
args.put("x-dead-letter-exchange", dlxExchangeName);
// 为这个DLX指定路由键,不指定则使用原队列的路由键
args.put("x-dead-letter-routing-key", dlxRoutingKey);
return new Queue(normalQueueName, true, false, false, args);
}
@Bean
Binding bindingDlxNormalQueue(Queue dlxNormalQueue, DirectExchange dlxNormalExchange) {
return BindingBuilder.bind(dlxNormalQueue).to(dlxNormalExchange).with(normalRoutingKey);
}
@Bean
public DirectExchange dlxExchange() {
return new DirectExchange(dlxExchangeName);
}
@Bean
public Queue dlxQueue() {
// true 持久化
return new Queue(dlxQueueName, true);
}
@Bean
Binding bindingDlxQueue(Queue dlxQueue, DirectExchange dlxExchange) {
return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
}
(2)测试发送消息
正常发送消息即可,不需要设置其他消息属性。
springboot-rabbitmq-demo测试代码:https://github.com/mytt-10566/springboot-rabbitmq-demo