死信队列
消息传输过程中难免会产生一些无法及时处理的消息,这些暂时无法处理的消息有时候也是需要被保留下来的,于是这些无法被及时处理的消息就变成了死信。
既然需要保留这些死信,那么就需要一个容器来存储它们以便后续需要时将它们取出来进行处理,于是就有了死信队列。
在RabbitMQ中当一个消息变成死信后会被重新发送到一个死信交换机(DLXs)中,当下列情况发生时队列中的消息会变成死信:
1:当消费端使用手动ack时,requeue属性为false时,消息被拒绝(basic.reject, basic.nack),换句话说就是消息被拒绝接收又不能回到原始队列中去
2:消息过期
3:队列超出最大限制导致消息无法发送到队列
死信交换机
死信交换机只是一个普通的交换机,它的声明使用与普通交换机没有什么区别
对于任意的队列,一个死信交换机可以被定义通过客户端使用队列参数,或者在服务端使用策略(polices),强烈建议使用polices的方式配置死信交换机,因为它不需要修改客户端代码,也不需要重启服务。
下面介绍使用RabbitMQ的管理界面进行死信队列的配置
- 进入policies配置页面
- 配置死信策略
- 在交换机界面配置死信交换机
- 在队列页面新建一个用于接收死信的死信队列
- 配置死信交换机的路由规则
- 配置完成
测试
生产者代码
public class DeadSender {
private static final String EXCHANGE_NAME = "amqp.car";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.135.88");
factory.setUsername("admin");
factory.setPassword("admin");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
//声明一个名为amqp.car的交换机,将消息发送至该交换机
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//每隔一秒发送一条消息,设置routind Key 为 car
while (true) {
channel.basicPublish(EXCHANGE_NAME, "car", null, "我是死信消息".getBytes("UTF-8"));
TimeUnit.SECONDS.sleep(1);
}
}
}
- 消费者代码,拒绝所有消息用于测试
public class DeadReceive {
private static final String EXCHANGE_NAME = "amqp.car";
public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.135.88");
factory.setUsername("admin");
factory.setPassword("admin");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
//声明一个队列用于接收消息
String queueName = "car.queue";
channel.queueDeclare(queueName, true, false, false, null);
//绑定队列,设置routing key为car
channel.queueBind(queueName, EXCHANGE_NAME, "car");
//消息接收后的回调方法
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" 收到信息:" +
delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");
//收到消息后直接拒绝,并设置requeue属性为false,这样被拒绝的消息就不会重新回到原始队列中而是转发到死信交换机
channel.basicNack(delivery.getEnvelope().getDeliveryTag(), false, false);
};
//接收消息,关闭自动ack
channel.basicConsume(queueName, false, deliverCallback, consumerTag -> { });
}
}
- 分别运行消费者与生产者,在RabbitMQ的管理界面观察队列的状态