死信队列: DLX,dead-letter-exchange
利用 dlx,当消息在一个队列中变成死信 (dead message) 之后,它能被重新 publish 到另一个 exchange,这个 exchange 就是 dlx
消息变成死信的原因有:
1.消息被拒绝 (basic.reject / basic.nack) 并且 reQueue=false
2.消息 TTL 过期
3.队列达到最大长度了
dlx 也是一个正常的 exchange,和一般的 exchange 没什么区别,它能在任何队列上被指定,实际上就是设置一个属性。
当这个队列中有死信时,rabbitMQ 就会自动的将这个消息重新发布到设置的 exchange 上去,进而被路由到另一个队列。
可以监听这个队列中消息做相应的处理,这个特性可以弥补 rabbitMQ3.0 以前支持的 immediate 参数功能。
死信队列的设置:
首先要设置死信队列的 exchange 和 queue,然后进行绑定:
exchange: dlx.exchange
queue: dlx.queue
routingkey: #
然后进行正常声明交换机、队列、绑定,只不过需要在队列加上一个参数即可: argument.put("x-dead-letter-exchange", "dlx.exchange");
这样消息在过期、reQueue、队列在达到最大长度时,消息就可以直接路由到死信队列
代码地址: https://github.com/hmilyos/rabbitmqdemo.git rabbitmq-api 项目下
public class Producer {
private static final Logger log = LoggerFactory.getLogger(Producer.class);
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(RabbitMQCommon.RABBITMQ_HOST);
connectionFactory.setPort(RabbitMQCommon.RABBITMQ_PORT);
connectionFactory.setVirtualHost(RabbitMQCommon.RABBITMQ_DEFAULT_VIRTUAL_HOST);
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//注意,这只是普通的交换机和routingKey
String exchange = "test_dlx_exchange";
String routingKey = "dlx.save";
for (int i = 0; i < 1; i++) {
String msg = "Hello RabbitMQ DLX Message" + i;
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.build();
log.info("生产端发送:{}", msg);
channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
}
}
}
/**
* 死信队列
*
*/
public class Consumer {
private static final Logger log = LoggerFactory.getLogger(Consumer.class);
public static void main(String[] args) throws IOException, TimeoutException {
//1 创建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost(RabbitMQCommon.RABBITMQ_HOST);
connectionFactory.setPort(RabbitMQCommon.RABBITMQ_PORT);
connectionFactory.setVirtualHost(RabbitMQCommon.RABBITMQ_DEFAULT_VIRTUAL_HOST);
//2 获取Connection
Connection connection = connectionFactory.newConnection();
//3 通过Connection创建一个新的Channel
Channel channel = connection.createChannel();
// 这就是一个普通的交换机 和 队列 以及路由
String exchangeName = "test_dlx_exchange";
String routingKey = "dlx.#";
String queueName = "test_dlx_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
Map agruments = new HashMap();
agruments.put("x-dead-letter-exchange", "dlx.exchange");
//这个agruments属性,要设置到声明队列上
channel.queueDeclare(queueName, true, false, false, agruments);
channel.queueBind(queueName, exchangeName, routingKey);
//要进行死信队列的声明: dlx.exchange/queue都是由你自己命名的,只不过为了这里只是为了简洁明了而已
channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
channel.queueDeclare("dlx.queue", true, false, false, null);
channel.queueBind("dlx.queue", "dlx.exchange", "#");
channel.basicConsume(queueName, true, new MyConsumer(channel));
log.info("消费端启动成功");
}
}
public class MyConsumer extends DefaultConsumer {
private static final Logger log = LoggerFactory.getLogger(MyConsumer.class);
public MyConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, // 消费者标签
Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
log.info("----dlx--MyConsumer-----consume message----------");
log.info("consumerTag: " + consumerTag);
log.info("envelope: " + envelope);
log.info("properties: " + properties);
log.info("body: " + new String(body));
}
}
先把消费端启动,去管控台查看 test_dlx_exchange 以及 test_dlx_queue 这两个普通的交换机、队列, 死信队列 dlx.queue 以及绑定的 dlx.exchange 是否创建成功
确认创建成功就关闭消费端,然后再启动生产端,这时候消息没被消费,一直在 test_dlx_queue 中,
直到设置的超时时间后,消息就被转发到死信队列中
自此,死信队列的简单用法介绍完毕。