RabbitMQ死信队列
死信,就是指那些无法被消费的消息,一般来说,producer 将消息投递到 broker 或者直接到 queue 里了,consumer 从 queue 取出消息进行消费,但某些时候由于特定的原因导致 queue 中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信自然就有了死信队列。
应用场景:为了保证订单业务的消息数据不丢失,需要使用到 RabbitMQ 的死信队列机制,当消息消费发生异常时,将消息投入死信队列中.还有比如说: 用户在商城下单成功并点击去支付后在指定时间未支付时自动失效。
死信的来源
1.消息 TTL 过期
2.队列达到最大长度(队列满了,无法再添加数据到 mq 中)
3.消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false
生产者代码
public class DeadProducer {
public static final String NORMAL_EXCHANGE = "normal_exchange";
public static final String NORMAL_QUEUE = "normal_queue";
public static final String ROUTING_KEY_NORMAL = "nobody_jack";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Channel channel = RabbitMqUtils.getChannel();
channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
//设置属性 当消息超过指定时间未被消费,则进入死信队列
AMQP.BasicProperties properties = new AMQP.BasicProperties()
.builder().expiration("10000").build();
channel.confirmSelect();
for(int i = 0; i <= 10; i++){
String msg = "test"+i;
boolean flag = channel.waitForConfirms();
if(flag){
System.out.println("消息发送完成:"+msg);
}
channel.basicPublish(NORMAL_EXCHANGE,ROUTING_KEY_NORMAL,properties,msg.getBytes(StandardCharsets.UTF_8));
}
}
}
消费者1
public class DeadQueueConsumer1 {
public static final String NORMAL_EXCHANGE = "normal_exchange";
public static final String DEAD_EXCHANGE = "dead_exchange";
public static final String NORMAL_QUEUE = "normal_queue";
public static final String DEAD_QUEUE = "dead_queue";
public static final String ROUTING_KEY_NORMAL = "nobody_jack";
public static final String ROUTING_KEY_DEAD = "bitch";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//声明交换机
channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
//设置死信队列进入的参数
Map<String,Object> params = new HashMap<>();
//设置交换机
params.put("x-dead-letter-exchange",DEAD_EXCHANGE);
//设置routingKey
params.put("x-dead-letter-routing-key",ROUTING_KEY_DEAD);
//超时时间
// params.put("x-message-ttl",10000);
//设置队列长度
params.put("x-max-length",8);
//声明普通队列
channel.queueDeclare(NORMAL_QUEUE,false,false,false,params);
channel.queueBind(NORMAL_QUEUE,NORMAL_EXCHANGE,ROUTING_KEY_NORMAL);
//声明死信队列
channel.queueDeclare(DEAD_QUEUE,false,false,false,null);
channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,ROUTING_KEY_DEAD);
System.out.println("队列1等待接收消息");
//消息回调
DeliverCallback deliverCallback = (consumerTag, message) -> {
try {
Thread.sleep(11000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String msg = new String(message.getBody(),"UTF-8");
System.out.println("普通队列的消息:"+msg);
//设置应答
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
};
channel.basicConsume(NORMAL_QUEUE,false,deliverCallback, consumerTag -> {
});
}
}
消费者2 消费私信队列消息
public class DeadQueueConsumer2 {
public static final String DEAD_EXCHANGE = "dead_exchange";
public static final String DEAD_QUEUE = "dead_queue";
public static final String ROUTING_KEY_DEAD = "bitch";
public static void main(String[] args) throws IOException, TimeoutException {
Channel channel = RabbitMqUtils.getChannel();
//声明交换机
channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
//声明死信队列
channel.queueDeclare(DEAD_QUEUE,false,false,false,null);
channel.queueBind(DEAD_QUEUE,DEAD_EXCHANGE,ROUTING_KEY_DEAD);
System.out.println("队列2等待接收消息");
DeliverCallback deliverCallback = (consumerTag, message) -> {
String msg = new String(message.getBody(),"UTF-8");
System.out.println("死信队列的消息:"+msg);
channel.basicAck(message.getEnvelope().getDeliveryTag(),false);
};
channel.basicConsume(DEAD_QUEUE,false,deliverCallback, consumerTag -> {
});
}
}
分别启动生产者和消费者1 消费者2
消费者1运行的过程中关闭服务
等待10秒后由死信队列2接收