RabbitMQ死信队列基础代码实现
RabbitMQ死信队列官网:https://www.rabbitmq.com/dlx.html
1.死信队列
1.1.死信队列介绍
死信,死掉的信息,就是消费者未处理就已经丢失,例如消费者未启动,生产者发出消息至交换机,交换机没有找到相应的队列,此消息就会丢失。如果这些消息很重要,而我们又需要,现在就有一种方法可将这些死信消息存下来,那就是死信交换机DLX(Dead Letter Exchanges),DLX也没那么复杂,它就是一个普通的交换机,它可以是Topic也可以是Fanout、Direct等类型。
实现步骤:
业务队列
里配置好死信交换机
和routing key
- 消息发送到
业务交换机
业务交换机
转发到业务队列
业务队列
将死信发送到死信交换机
- 再由
死信交换机
转发到死信队列
1.2.死信队列产生原因
由官网描述可知,死信队列产生原因有3条:
- 拒绝:消息被队列拒绝接收
- 超时:消息存活时间TTL(time to live)超时
- 长度:队列中的消息长度达到最大值
1.3.死信队列作用
延迟队列的定义与实现
-
定义:
延迟队列存储的对象肯定是对应的延时消息,所谓"延时消息"是指当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。
-
实现:
rabbitmq可以通过设置队列的
TTL
和死信路由
实现延迟队列- TTL:
RabbitMQ可以针对Queue设置x-expires 或者 针对Message设置 x-message-ttl,来控制消息的生存时间,如果超时(两者同时设置以最先到期的时间为准),则消息变为dead letter(死信)
- 死信路由DLX
RabbitMQ的Queue可以配置
x-dead-letter-exchange
和x-dead-letter-routing-key
(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。- x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
- x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
x-dead-letter-exchange
和 x-dead-letter-routing-key
为 k-v
键值对中的固定key,写法固定
延迟队列使用场景
为什么不能用定时任务完成?
如果恰好在一次扫描后完成业务逻辑,那么就会等待两个扫描周期才能扫到过期的订单,不能保证时效性
2.代码实现
大致流程:
2.1.消费者代码实现
/**
* Direct-消费者
*
* @Author: Ron
* @Create: 2021 11:12
*/
public class Recv {
// 死信队列
private final static String DEAD_QUEUE_NAME = "dead_queue";
// 业务队列
private final static String QUEUE_NAME = "msg_queue";
// 业务交换机
private final static String EXCHANGE_NAME = "msg_exchange";
// 死信交换机
private final static String DEAD_EXCHANGE_NAME = "dead_exchange";
public static void main(String[] argv) throws Exception {
// 获取到连接
Connection connection = ConnectionUtil.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 交业务换机声明
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 指定死信发送的死信交换机DLX
Map<String, Object> agruments = new HashMap<String, Object>();
agruments.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
agruments.put("x-dead-letter-routing-key", "dead");
//声明一个业务队列,为业务队列配置死信交换机和路由key
channel.queueDeclare(QUEUE_NAME, true, false, false, agruments);
// 绑定队列到交换机,同时指定需要订阅的routing key。订阅
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "msg");
// 声明死信交换机
channel.exchangeDeclare(DEAD_EXCHANGE_NAME, "direct");
// 声明死信队列,存放死信消息
channel.queueDeclare(DEAD_QUEUE_NAME, true, false, false, null);
//将DLX和死信存放队列绑定,并产生一个路由key
channel.queueBind(DEAD_QUEUE_NAME, DEAD_EXCHANGE_NAME, "dead");
// 定义队列的消费者
DefaultConsumer consumer = new DefaultConsumer(channel) {
// 获取消息,并且处理,这个方法类似事件监听,如果有消息的时候,会被自动调用
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
// body 即消息体
String msg = new String(body);
System.out.println(" [消费者2] received : " + msg + "!");
}
};
// 监听队列,自动ACK
channel.basicConsume(QUEUE_NAME, true, consumer);
}
}
启动消费者
生成的交换机:
生成的队列:
2.2.生产者代码实现
/**
* Direct-生产者
* @Author: Ron
* @Create: 2021 11:12
*/
public class Send {
// 业务交换机
private final static String EXCHANGE_NAME = "msg_exchange";
public static void main(String[] argv) throws Exception {
// 获取到连接
Connection connection = ConnectionUtil.getConnection();
// 获取通道
Channel channel = connection.createChannel();
// 声明exchange,指定类型为direct
// 消息内容
String message = "死信队列消息发送测试。。。";
// 设置消息存活时间ttl为10秒
AMQP.BasicProperties properties = new AMQP.BasicProperties
.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.build();
// 消息发送至业务交换机
channel.basicPublish(EXCHANGE_NAME, "msg",true, properties, message.getBytes());
System.out.println(" [商品服务:] Sent '" + message + "'");
channel.close();
connection.close();
}
}
关闭消费者代码,运行生产者代码,模拟消息未被消费者处理,超时
如果队列内出现了dead letter,则按照x-dead-letter-exchange
和x-dead-letter-routing-key
这两个参数重新路由转发到指定的队列
最后,再通过监听该死信队列
的消息来实现对消息的处理
查看死信队列
中的具体信息: