6 死信队列
6.1 死信队列概念
死信,顾名思义就是无法被消费的消息,字面意思可以这样理解,一般来说,producer将消息投递到 broker或者直接到queue里了,consumer 从 queue取出消息进行消费,但某些时候由于特定的原因导致queue中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有了死信队列。
6.2 死信的来源
- 消息TTL过期
- 队列满了,无法再添加数据到mq中
- 消息被拒绝,否定应答并且没有重新入队
6.3 案例
6.3.1 消息TTL过期
启动Consumer1,关闭Consumer1;启动producer,模拟发送死信消息;启动Consumer2,消费死信消息
/**
* 死信队列 案例消费者1
*/
public class Consumer1 {
//普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
//死信交换机
public static final String DEAD_EXCHANGE = "dead_exchange";
//普通队列
public static final String NORMAL_QUEUE = "normal_queue";
//死信队列
public static final String DEAD_QUEUE = "dead_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//声明普通交换机
channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
//声明死信交换机
channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
Map<String, Object> arguments = new HashMap<>();
//过期时间 10s,也可以在生产者指定过期时间(推荐)
//arguments.put("x-message-ttl",10000);
//指定正常队列的死信交换机
arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);
//指定死信交换机绑定的routingKey
arguments.put("x-dead-letter-routing-key", "lisi");
//死信队列和普通队列 1:队列名 2:持久化 3:共享 4:自动删除 5:参数
channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);
channel.queueDeclare(DEAD_QUEUE, false, false, false, null);
//绑定普通交换机和队列 1:交换机名 2:队列名 3:routingKey
channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,"zhangsan");
//绑定死信交换机和队列
channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,"lisi");
System.out.println("等待接收消息...");
//接收消息
channel.basicConsume(NORMAL_QUEUE, true, (consumerTage, message) ->
System.out.println("Consumer1接受到的消息:" + new String(message.getBody())), (consumer) -> {
});
}
}
/**
* 死信队列生产者
*/
public class Producer {
//普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//死信消息 ttl time to live
AMQP.BasicProperties properties =
new AMQP.BasicProperties().builder().expiration("10000").build();
for (int i = 0; i < 10; i++) {
String message = "info" + i;
channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties, message.getBytes(StandardCharsets.UTF_8));
}
}
}
/**
* 死信队列 案例消费者2
*/
public class Consumer2 {
//死信队列
public static final String DEAD_QUEUE = "dead_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
System.out.println("等待接收消息...");
//接收消息
channel.basicConsume(DEAD_QUEUE, true, (consumerTage, message) ->
System.out.println("Consumer2接受到的消息:" + new String(message.getBody())), (consumer) -> {
});
}
}
6.3.2 队列达到最大长度
//Consumer1添加设置正常队列的最大长度
arguments.put("x-max-length",6);
/**
* 死信队列生产者
*/
public class Producer {
//普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
for (int i = 0; i < 10; i++) {
String message = "info" + i;
channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", null,
message.getBytes(StandardCharsets.UTF_8));
}
}
}
6.3.3 消息被拒绝
消息先恢复为空,删除掉normal_queue
/**
* 死信队列 案例消费者1
*/
public class Consumer1 {
//普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
//死信交换机
public static final String DEAD_EXCHANGE = "dead_exchange";
//普通队列
public static final String NORMAL_QUEUE = "normal_queue";
//死信队列
public static final String DEAD_QUEUE = "dead_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//声明普通交换机
channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
//声明死信交换机
channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
Map<String, Object> arguments = new HashMap<>();
//过期时间 10s,也可以在生产者指定过期时间(推荐)
//arguments.put("x-message-ttl",10000);
//指定正常队列的死信交换机
arguments.put("x-dead-letter-exchange", DEAD_EXCHANGE);
//指定死信交换机绑定的routingKey
arguments.put("x-dead-letter-routing-key", "lisi");
//设置正常队列的最大长度
//arguments.put("x-max-length",6);
//死信队列和普通队列 1:队列名 2:持久化 3:共享 4:自动删除 5:参数
channel.queueDeclare(NORMAL_QUEUE, false, false, false, arguments);
channel.queueDeclare(DEAD_QUEUE, false, false, false, null);
//绑定普通交换机和队列 1:交换机名 2:队列名 3:routingKey
channel.queueBind(NORMAL_QUEUE, NORMAL_EXCHANGE, "zhangsan");
//绑定死信交换机和队列
channel.queueBind(DEAD_QUEUE, DEAD_EXCHANGE, "lisi");
System.out.println("等待接收消息...");
//接收消息 手动应答
channel.basicConsume(NORMAL_QUEUE, false, (consumerTage, message) -> {
String msg = new String(message.getBody());
if ("info5".equals(msg)) {
System.out.println("Consumer1接受到的消息:" + msg + ",此消息被拒绝!");
//拒绝接收不重新入队 1:消息标签 2:是否重新入队
channel.basicReject(message.getEnvelope().getDeliveryTag(), false);
} else {
System.out.println("Consumer1接受到的消息:" + msg);
channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
}
}, (consumer) -> {
});
}
}
/**
* 死信队列生产者
*/
public class Producer {
//普通交换机
public static final String NORMAL_EXCHANGE = "normal_exchange";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//死信消息 ttl time to live
for (int i = 0; i < 10; i++) {
String message = "info" + i;
channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", null,
message.getBytes(StandardCharsets.UTF_8));
}
}
}
/**
* 死信队列 案例消费者2,消费死信队列信息
*/
public class Consumer2 {
//死信队列
public static final String DEAD_QUEUE = "dead_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
System.out.println("等待接收消息...");
//接收消息
channel.basicConsume(DEAD_QUEUE, true, (consumerTage, message) ->
System.out.println("Consumer2接受到的消息:" + new String(message.getBody())), (consumer) -> {
});
}
}