死信队列是RabbitMQ中非常重要的一个特性。简单理解,他是RabbitMQ对于未能正常消费的消息进行的 一种补救机制。死信队列也是一个普通的队列,同样可以在队列上声明消费者,继续对消息进行消费处理。 对于死信队列,在RabbitMQ中主要涉及到几个参数
重点:核心就在这里:
x-dead-letter-exchange: mirror.dlExchange 对应的死信交换机
x-dead-letter-routing-key: mirror.messageExchange1.messageQueue1 死信交换机routingkey
x-message-ttl:3000 消息过期时间
durable: true 持久化,这个是必须的。
如果依然不知道在哪里设置,看下图:创建一个队列的时候有参数可以设置
在这里,x-dead-letter-exchange指定一个交换机作为死信交换机,然后x-dead-letter-routing-key指定交 换机的RoutingKey。而接下来,死信交换机就可以像普通交换机一样,通过RoutingKey将消息转发到对应 的死信队列中。
目录
1、何时会产生死信
2、死信队列的配置方式
3、关于参数x-dead-letter-routing-key
4、如何确定一个消息是不是死信
5、基于死信队列实现延迟队列
有以下三种情况,RabbitMQ会将一个正常消息转成死信:
一:消息被消费者确认拒绝。消费者把requeue参数设置为false,也就是不重新入队,并且在消费后,向RabbitMQ返回 拒绝。channel.basicReject或者channel.basicNack。
二:消息达到预设的TTL时限还一直没有被消费。
三:消息由于队列已经达到最长长度限制而被丢掉。
TTL即最长存活时间 Time-To-Live 。消息在队列中保存时间超过这个TTL,即会被认为死亡。死亡的消 息会被丢入死信队列,如果没有配置死信队列的话,RabbitMQ会保证死了的消息不会再次被投递,并 且在未来版本中,会主动删除掉这些死掉的消息。 设置TTL有两种方式,一是通过配置策略指定,另一种是给队列单独声明TTL
策略配置方式 - Web管理平台配置 或者 使用指令配置 60000为毫秒单位 在声明队列时指定 - 同样可以在Web管理平台配置,也可以在代码中配置:
Map args = new HashMap();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);
RabbitMQ中有两种方式可以声明死信队列,一种是针对某个单独队列指定对应的死信队列。另一种就是以 策略的方式进行批量死信队列的配置。 针对多个队列,可以使用策略方式,配置统一的死信队列。
rabbitmqctl set_policy DLX ".*" '{"dead-letter-exchange":"my-dlx"}' --apply-to queues
针对队列单独指定死信队列的方式主要是之前提到的三个属性。
channel.exchangeDeclare("some.exchange.name", "direct");
Map args = new HashMap();
args.put("x-dead-letter-exchange", "some.exchange.name");
channel.queueDeclare("myqueue", false, false, false, args);
这些参数,也可以在RabbitMQ的管理页面进行配置。另外,你会注意到,在对队列进行配置时,只有Classic经典队列和Quorum仲裁队列才能配置死信队列, 而目前Stream流式队列,并不支持配置死信队列。
死信在转移到死信队列时,他的Routing key 也会保存下来。但是如果配置了x-dead-letter-routing-key这 个参数的话,routingkey就会被替换为配置的这个值。 另外,死信在转移到死信队列的过程中,是没有经过消息发送者确认的,所以并不能保证消息的安全性。
消息被作为死信转移到死信队列后,会在Header当中增加一些消息。在官网的详细介绍中,可以看到很多 内容,比如时间、原因(rejected,expired,maxlen)、队列等。然后header中还会加上第一次成为死信的三个 属性,并且这三个属性在以后的传递过程中都不会更改。
x-first-death-reason
x-first-death-queue
x-first-death-exchange
其实从前面的配置过程能够看到,所谓死信交换机或者死信队列,不过是在交换机或者队列之间建立一种 死信对应关系,而死信队列可以像正常队列一样被消费。他与普通队列一样具有FIFO的特性。对死信队列的 消费逻辑通常是对这些失效消息进行一些业务上的补偿
RabbitMQ中,是不存在延迟队列的功能的,而通常如果要用到延迟队列,就会采用TTL+死信队列的方 式来处理。
最后,用代码实现一个延时队列的功能:Springboot +rabbitmq
配置类:
@Configuration
public class DeadLetterMsg {
@Bean
public Queue orderQueue(){
Map params = new HashMap<>();
params.put("x-dead-letter-exchange", MyConstants.DEAD_LETTER_EXCHANGE);
params.put("x-dead-letter-routing-key",MyConstants.DEAD_KEY);
params.put("x-message-ttl",30000);
return new Queue(MyConstants.ORDER_QUEUE,true,false,false,params);
}
@Bean
public DirectExchange orderDirectExchange(){
return new DirectExchange(MyConstants.ORDER_EXCHANGE);
}
@Bean
public Binding orderBinding(){
return BindingBuilder.bind(orderQueue()).to(orderDirectExchange()).with(MyConstants.ORDER_KEY);
}
@Bean
public Queue deadQueue(){
return new Queue(MyConstants.DEAD_LETTER_QUEUE,true,false,false);
}
@Bean
public DirectExchange deadDirectExchange(){
return new DirectExchange(MyConstants.DEAD_LETTER_EXCHANGE);
}
@Bean
public Binding deadBinding(){
return BindingBuilder.bind(deadQueue()).to(deadDirectExchange()).with(MyConstants.DEAD_KEY);
}
}
发送
@RestController
@Api(value = "自己学习rabbitmq",tags = "学习mq动手")
public class XssProducer {
@Autowired
private RabbitTemplate rabbitTemplate ;
@RequestMapping(value = "/sendOrder",method = RequestMethod.GET)
@ApiOperation(value="订单",notes="sendTopic")
@ApiImplicitParam(name="msg",value="要发送的信息",defaultValue = "你好,订单")
public void sendOrder(String msg){
int random = new Random().nextInt();
rabbitTemplate.send(MyConstants.ORDER_EXCHANGE,MyConstants.ORDER_KEY
,new Message(("订单信息No:123456" +random).getBytes(StandardCharsets.UTF_8)));
}
}
消费死信队列:
@Component
public class DeadConsumer {
@RabbitListener(queues = {MyConstants.DEAD_LETTER_QUEUE})
public void receiveDeadLetterQueue(String message){
System.out.println(" 死信队列中的消息为:" + message);
}
}
运行代码就会发现30s后会收到之前发送到orderQueue的信息。注意:这30s中内orderQueue别被消费了。