死信队列(DLX)
这个概念 在其他MQ产品里面也是有的,只不过在Rabbitmq中稍微特殊一点
什么叫私信队列呢? 就是当消息成为 dead message之后,可以重新发到另外一台交换机,这个交换机就是DLX。
注意这里的有翻译歧义, 这里的DLX 指的是 交换机 ,而不是一个队列。
消息成为死信队列的三种情况
-
队列的消息长度 到达限制。
-
消费者拒收消息,
channel.basicNack(deliveryTag,true,false);
//这里拒收消息
/**
* deliveryTag 标识
* requeue 是否打回原队列如果为false 则进入死信队列
*/
channel.basicReject(deliveryTag,false);
}
- 存在消息过期设置 消息超时未消费(就是上一篇中的TTL)
我们拿这个TTL举例:
首先你在发送端 需要有2个队列 一个正常队列 一个私信队列
然后你在创建延时队列的时候 要注明 这个队列的消息一旦超时 那么送到哪个死信交换机的队列里面去:
@Configuration
public class RabbitMQConfig {
public static final String EXCHANGE_NAME = "boot_topic_exchange";
public static final String QUEUE_NAME_TTL = "boot_queue_ttl";
public static final String DEAD_EXCHANGE_NAME = "dead_topic_exchange";
public static final String DEAD_QUEUE_NAME = "dead_queue";
//正常交换机
@Bean("bootExchange")
public Exchange bootExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
@Bean("bootQueue")
public Queue bootQueue(){
Map<String, Object> map = new HashMap<>();
// 设置TTL
map.put("x-message-ttl", 20000);
// 设置死信的目的交换机
map.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
// 设置死信交给目的交换机时的路由键
map.put("x-dead-letter-routing-key", "boot.111");
return QueueBuilder.durable(QUEUE_NAME_TTL).withArguments(map).build();
}
@Bean
public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue,@Qualifier("bootExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
}
//死信交换机
@Bean("deadExchange")
public Exchange deadExchange(){
return ExchangeBuilder.topicExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
@Bean("deadQueue")
public Queue deadQueue(){
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
@Bean
public Binding bindDeadQueueExchange(@Qualifier("deadQueue") Queue queue,@Qualifier("deadExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("boot.*").noargs();
}
}
好了 我们发送消息 往正常队列里面发:
看超时之后 它进来了:
·这是超时的消息进入DLX的情况
再动动手 我们实践一下 消费者拒收之后 进入死信的情况:
我们监听正常的队列 然后接受到消息之后
我们故意写一个bug 触发exception
调用basicNack方法 打回这个消息 让它 进入dlx
这里千万要注意:basicNack第三个参数要为false 才会进入dlx
如果为true 那它就会打回到正常队列 然后不停的给你发
@RabbitListener(queues = "boot_queue_ttl")
public void ListenerQueue(Message message,
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag,
Channel channel) throws IOException {
try {
System.out.println("收到消息为"+new String(message.getBody()));
System.out.println("处理业务逻辑。。");
// int i =3/0; //手动制造一个bug 让程序进入异常
channel.basicAck(deliveryTag,true);
}catch (Exception e){
//拒绝签收
//这里的第三个参数表示 如果为true 消息重回到queue broker会重新发送该消息给消费端
System.out.println(" error 处理业务逻辑失败 消息打回 进入私信队列");
channel.basicNack(deliveryTag,true,false);
}
ok我们实践了两种 第三种 超过队列长度的很简单 你设一个队列长度max=10 然后for循环往里面塞进去>10 超过的就会进入dlx 这种很简单
有兴趣的好兄弟 可以自己去试一下啦
新手一定要多动手
延迟队列
什么是延迟队列 就是交换机收到消息之后 它不急着发 它等。
等到你想要它发给消费者方的时候 你再发。
假设你正在开发一个电子商务网站,并且你希望为用户提供一个订单确认后一段时间自动取消的功能。当用户下单后,订单将被发送到延迟队列中,设置一个特定的延迟时间,例如30分钟。如果在30分钟内用户未支付订单,系统将自动取消该订单。
但是问题来了 rabbitmq 中并没有提供现成的延迟队列实现,
需要我们自己去实现:
怎么实现呢 如果你理解熟悉了 前面的TTL 和死信队列之后
这个就非常非常简单了
看上面那个我们死信队列里面那个例子:
发送端向正常队列里面发数据 超时未消费。 消息就进入了死信队列,
那就很简单了 你把消费的监听队列 换成死信队列 就ok了
他就是一个延迟队列了