06-RabbitMQ死信交换机
1.死信交换机介绍
当一个队列中信息满足下列情况之一时,可以成为死信(dead letter)
&消费者使用basic.reject(RejectAndDontRequeueRecoverer处理策略)或bastic.nack(ImmediateRequeueMessageRecoverer处理策略)消费失败,requeue(重新入队)设置参数为false;
&消息是一个过期消息,过期无人消费;
&要投递的队列消息堆积满了,最早的消息成为死信;
如果该队列配置了dead-letter-exchange属性,指定交换机,队列设置dead-letter-routing-key属性,设置死信交换机与死信队列的RoutingKey,那么队列中的死信就会投递到这个交换机中,而这个交换机成为死信交换机(Dead Letter Exchange,简称DLX)。

2.TTL
TTL,也就是Time-To-Live。如果队列中的消息TTL结束仍未消费则会成为死信,TTL超时分为两种情况:
&消息所在的队列设置了存活时间;
&消息本身设置了存活时间;

3.代码实现
声明一组死信交换机和队列,注解模式:
@RabbitListener(bindings = @QueueBinding( value = @Queue(name = "dl.queue", durable = "true"), exchange = @Exchange(name = "dl.direct"), key = "dl")) public void listenDlQueue(String msg){ log.info("接收到 dl.queue的延迟消息:{}", msg); }
要给队列设置超时时间,需要在声明队列时配置x-message-ttl属性:
@Bean public Queue ttlQueue() { return QueueBuilder.durable("ttl.queue") //指定队列名称,并持久化 .ttl(10000) //设置队列的超时时间,10秒 .deadLetterExchange("dl.direct") //指定死信交换机 .deadLetterRoutingKey("dl") //指定死信RoutingKey .build(); }
@Bean public Binding simpleBinding() { return BindingBuilder.bind(ttlQueue()) .to(ttlExchange()).with("ttl"); }
发送消息时给消息本身设置超时时间:
@Test
public void testTTLMsg() {
// 创建消息
Message message = MessageBuilder
.withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8))
.setExpiration("5000")
.build(); // 消息ID,需要封装到CorrelationData中
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息
rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
}
消息超时的两种方式:
&给队列设置超时时间ttl,进入队列超过ttl的消息变为死信;
&给消息设置超时时间,队列接受消息超过ttl时间未消费;
&两者共存时,以ttl时间短的为准。
4..延迟队列
利用ttl结合死信交换机,实现了消息发出后,消费者延迟收到消息的结果,这种消息队列成为延迟队列(Delay Queue)模式。
延迟队列使用的场景:
&用户下单后超过15分钟未支付取消支付;
&预约20分钟会议,到时间自动通知参会人员;
延迟队列插件:RabbitMQ官方推出了DelayExchange插件
SpringAMQP使用延迟队列插件:
DelayExchange的本质还是三种交换机,只是添加了延迟功能,因此使用时只需要声明一个交换机,交换机可以是任意类型,然后设定delayed属性为true即可。
基于注解方式实现:
@RabbitListener(bindings = @QueueBinding( value = @Queue(name = "delay.queue", durable = "true"), exchange = @Exchange(name = "delay.direct", delayed = "true"), key = "delay")) public void listenDlQueue(String msg) { log.info("接收到 delay.queue的延迟消息:{}", msg); }
然后我们向这个delay为true的交换机中发送消息,一定要给消息添加一个header:x-delay,值为延迟的时间,单位为毫秒:
@Test public void testTTLMsg() { // 创建消息 Message message = MessageBuilder .withBody("hello, ttl message".getBytes(StandardCharsets.UTF_8)) .setHeader("x-delay",10000) .build(); // 消息ID,需要封装到CorrelationData中 CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());// 发送消息 rabbitTemplate.convertAndSend("delay.direct", "ttl", message, correlationData); }
5.消息堆积问题
当生产者发送的消息的速度大于消费者消费的速度,就会导致队列中的消息堆积,直到达到消息的上限,最早接受的消息,可能会被丢弃,成为死信。
解决消费堆积的三种策略:
&增加消费者,增加消费速度
&在消费者内开启线程池加快消息处理速度
&扩大队列的容积
6.惰性队列
从RabbitMq的3.6.0版本开始增加了Lazy Queues的概念,也就是惰性队列。
惰性队列的特征如下:
&接受消息后直接存入磁盘而非内存;
&消费者消费消息才会中磁盘中将消息加载到内存;
&支持数百万的数据存储;
用StringAMQP声明队列的两种方式:
@Bean的方式:
@Bean public Queue lazyQueue() { return QueueBuilder.durable("lazy.queue") .lazy()//开启惰性队列x-queue-mode .build(); }
注解方式:
@RabbitListener(queuesToDeclare = { @Queue(name = "lazy.queue", declare = "true", arguments = @Argument(name = "x-queue-mode", value = "lazy")) }) public void listenLazyQueue(String msg) { System.out.println("消费者接收到lazy.queue的消息:【" + msg + "】"); }

浙公网安备 33010602011771号