RabbitMQ学习-死信队列

死信队列

背:就是三种情况导致消息无法消费就是死信,然后就会转到死信交换机中,死信交换机发送到死信队列中,然后创建个消费者消费死信队列中的东西,再没什么哈哈

死信,顾名思义就是无法被消费的信息,字面意思就是这样理解,一般来说,消息投递到队列中,消费者从队列取出消息进行消费,但某些时候由于特定的原因导致队列中的某些消息无法被消费,这样的消息如果没有后续的处理,就变成了死信,有死信就有了死信队列

消息成为死信的三种情况:

1.队列消息数量到达限制:比如队列最大只能存储10条消息,而发了11条数据,根据先进先出,最先发的消息会进入死信队列

2.消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false

3.原队列存在消息过期设置,消息达到超时时间为被消费

通常情况下,消费者是能正常消费的,但是出现上面说的三种情况之一,就无法正常消费信息,消息就会进入死信交换机,死信交换机和死信队列进行绑定,最后由其他消费者来消费死信消息

RabbitMQ学习-死信队列_第1张图片

死信的产生既然不可避免,那么就需要从实际的业务角度和场景出发,对这些死信进行后续的处理,常见的处理方式大致有下面几种,

① 丢弃,如果不是很重要,可以选择丢弃

② 记录死信入库,然后做后续的业务分析或处理

③ 通过死信队列,由负责监听死信的应用程序进行处理

综合来看,更常用的做法是第三种,即通过死信队列,将产生的死信通过程序的配置路由到指定的死信队列,然后应用监听死信队列,对接收到的死信做后续的处理。

队列绑定死信交换机:

给队列设置参数:x-dead-letter-exchange 和x-dead-letter-routing-key

消息TTl过期

设置TTL的两种方式:

1.队列设置TTL

在创建对别的收设置队列的x-message-ttl属性,例如

Map map = new HashMap<>();
//设置队列有效期为10秒
map.put("x-message-ttl",10000);
channel.queueDeclare(queueName,durable,exclusive,autoDelete,map);

消息设置TTL
对每条消息设置TTL
AMQP.BasicProperties properties = new AMQP.BasicProperties().builder().expiration("10000").build();
 channel.basicPublish(exchangeName,routingKey,mandatory,properties,"msg body".getBytes());
两者之间的区别:
1.如果设置了队列的TTL属性,那么一旦消息过期,就会被队列的丢弃
2.如果是消息设置了TTL属性,那么即使消息过期,也不一定会马上丢弃,因为消息是否过期是在即将投递到消费者之前判定的,如果当前队列有严重的消息挤压情况,那么已
经过期的消息也许还能存活较长时间
3.如果我们没有设置TTL,就表示消息永远不会过期,如果TTL设置为0,则表示除非此时可以直接投递到消费者,否则该消息会被丢弃
生产者代码:
public class Producer {
 private static final String NORMAL_EXCHANGE = "normal_exchange";
 public static void main(String[] argv) throws Exception {
 try (Channel channel = RabbitMqUtils.getChannel()) {
 channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
 //设置消息的 TTL 时间
 AMQP.BasicProperties properties = new 
AMQP.BasicProperties().builder().expiration("10000").build();
 //该信息是用作演示队列个数限制
 for (int i = 1; i <11 ; i++) {
 String message="info"+i;
 channel.basicPublish(NORMAL_EXCHANGE, "zhangsan", properties, 
message.getBytes());
 System.out.println("生产者发送消息:"+message);
 }
 }
 }
}

消费者 C1 代码(启动之后关闭该消费者 模拟其接收不到消息)

public class Consumer01 {
 //普通交换机名称
 private static final String NORMAL_EXCHANGE = "normal_exchange";
 //死信交换机名称
 private static final String DEAD_EXCHANGE = "dead_exchange";
 public static void main(String[] argv) throws Exception {
 Channel channel = RabbitUtils.getChannel();
//声明死信和普通交换机 类型为 direct
 channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
 channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
 //声明死信队列
 String deadQueue = "dead-queue";
 channel.queueDeclare(deadQueue, false, false, false, null);
 //死信队列绑定死信交换机与 routingkey
 channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
 //正常队列绑定死信队列信息
 Map params = new HashMap<>();
 //正常队列设置死信交换机 参数 key 是固定值
 params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
 //正常队列设置死信 routing-key 参数 key 是固定值
 params.put("x-dead-letter-routing-key", "lisi");
 
 String normalQueue = "normal-queue";
 channel.queueDeclare(normalQueue, false, false, false, params);
 channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
 System.out.println("等待接收消息.....");
 DeliverCallback deliverCallback = (consumerTag, delivery) -> {
 String message = new String(delivery.getBody(), "UTF-8");
 System.out.println("Consumer01 接收到消息"+message);
 };
 channel.basicConsume(normalQueue, true, deliverCallback, consumerTag -> {
 });
 }
}

RabbitMQ学习-死信队列_第2张图片

C2 代码(以上步骤完成后 启动 C2 消费者 它消费死信队列里面的消息)  

public class Consumer02 {
 private static final String DEAD_EXCHANGE = "dead_exchange";
 public static void main(String[] argv) throws Exception {
Channel channel = RabbitUtils.getChannel();
 channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
 String deadQueue = "dead-queue";
 channel.queueDeclare(deadQueue, false, false, false, null);
 channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
 System.out.println("等待接收死信队列消息.....");
 DeliverCallback deliverCallback = (consumerTag, delivery) -> {
 String message = new String(delivery.getBody(), "UTF-8");
 System.out.println("Consumer02 接收死信队列的消息" + message);
 };
 channel.basicConsume(deadQueue, true, deliverCallback, consumerTag -> {
 });
 }
}

RabbitMQ学习-死信队列_第3张图片

 队列达到最大长度

1. 消息生产者代码去掉 TTL 属性
public class Producer {
 private static final String NORMAL_EXCHANGE = "normal_exchange";
 public static void main(String[] argv) throws Exception {
 try (Channel channel = RabbitMqUtils.getChannel()) {
 channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
 //该信息是用作演示队列个数限制
 for (int i = 1; i <11 ; i++) {
 String message="info"+i;
 channel.basicPublish(NORMAL_EXCHANGE,"zhangsan",null, message.getBytes());
 System.out.println("生产者发送消息:"+message);
 }
 }
 }
}
2. C1 消费者修改以下代码 ( 启动之后关闭该消费者 模拟其接收不到消息 )
RabbitMQ学习-死信队列_第4张图片

注意此时需要把原先队列删除 因为参数改变了 

3. C2 消费者代码不变 ( 启动 C2 消费者)
RabbitMQ学习-死信队列_第5张图片

消息被拒

1.消息生产者代码同上生产者一致

2.C1 消费者代码(启动之后关闭该消费者 模拟其接收不到消息)

 //普通交换机名称
 private static final String NORMAL_EXCHANGE = "normal_exchange";
 //死信交换机名称
 private static final String DEAD_EXCHANGE = "dead_exchange";
 public static void main(String[] argv) throws Exception {
 Channel channel = RabbitUtils.getChannel();
 //声明死信和普通交换机 类型为 direct
 channel.exchangeDeclare(NORMAL_EXCHANGE, BuiltinExchangeType.DIRECT);
 channel.exchangeDeclare(DEAD_EXCHANGE, BuiltinExchangeType.DIRECT);
 //声明死信队列
 String deadQueue = "dead-queue";
 channel.queueDeclare(deadQueue, false, false, false, null);
 //死信队列绑定死信交换机与 routingkey
 channel.queueBind(deadQueue, DEAD_EXCHANGE, "lisi");
//正常队列绑定死信队列信息
 Map params = new HashMap<>();
 //正常队列设置死信交换机 参数 key 是固定值
 params.put("x-dead-letter-exchange", DEAD_EXCHANGE);
 //正常队列设置死信 routing-key 参数 key 是固定值
 params.put("x-dead-letter-routing-key", "lisi");
 String normalQueue = "normal-queue";
 channel.queueDeclare(normalQueue, false, false, false, params);
 channel.queueBind(normalQueue, NORMAL_EXCHANGE, "zhangsan");
 System.out.println("等待接收消息.....");
 DeliverCallback deliverCallback = (consumerTag, delivery) -> {
 String message = new String(delivery.getBody(), "UTF-8");
 if(message.equals("info5")){
 System.out.println("Consumer01 接收到消息" + message + "并拒绝签收该消息");
 //requeue 设置为 false 代表拒绝重新入队 该队列如果配置了死信交换机将发送到死信队列中
 channel.basicReject(delivery.getEnvelope().getDeliveryTag(), false);
 }else {
 System.out.println("Consumer01 接收到消息"+message);
 channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
 }
 };
 boolean autoAck = false;
 channel.basicConsume(normalQueue, autoAck, deliverCallback, consumerTag -> {
 });
 }
}

 RabbitMQ学习-死信队列_第6张图片

3. C2 消费者代码不变

启动消费者 1 然后再启动消费者 2

RabbitMQ学习-死信队列_第7张图片

你可能感兴趣的:(java-rabbitmq,rabbitmq,学习)