RabbitMQ中的死信及死信队列详解

1、什么是死信

死信产生主要来自于两个过程角色

  • 来自于消费者端
  • 来自于queue

产生死信的三种情况:

  • 如果queue中的消息被消费者接收, 但是消费者拒绝消费(消费者执行了reject 或nack 并将 requee 参数设置为 false )的时候,这个消息就会变成死信。
  • 消息本身设置了过期时间(TTL), 并且消息过期时间已经生效, 还未被消费的消息就会变成死信【特点是每个消息的过期时间都不同】
  • 可以设置队列中所有消息的过期时间,如果消息过期时间已经生效,消息还未被消费
  • 队列设置了最大长度限制, 当队列已满, 之后从交换机路由到该队列的消息会自动变成死信。

2、什么是死信交换机

死信交换机是专门处理死信的交换机

3、什么是死信队列

跟死信交换机绑定的队列就是死信队列,死信队列中存储的消息都是死信消息。

4、死信队列的应用

  • 保证消息不会丢失,保证数据的完整性;
  • 可以借助延时消费的特性完成特定的功能(比如订单生成但是未支付,超过30分钟自动取消的业务场景)

5、原理图介绍

在这里插入图片描述

6、三种场景代码演示

6.1 消费者拒绝消费消息, 消息进入死信队列

首先需要在application.yml文件中开启手动ack

spring:
  rabbitmq:
    # 开启消费者手动ack
    listener:
      direct:
        acknowledge-mode: manual

创建配置类
往容器中注入普通交换机、普通队列、路由规则和死信交换机和死信队列、死信路由规则

package com.kkarma.config;


import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DeadLettingConfig {


    public static final String GENERIC_EXCHANGE = "generic-exchange";
    public static final String GENERIC_QUEUE = "generic-queue";
    public static final String GENERIC_ROUTING_KEY = "generic.#";


    public static final String DEAD_EXCHANGE = "dead-exchange";
    public static final String DEAD_QUEUE = "dead-queue";
    public static final String DEAD_ROUTING_KEY = "dead.#";

    @Bean
    public Exchange genericExchange(){
        return ExchangeBuilder.topicExchange(GENERIC_EXCHANGE).build();
    }

    @Bean
    public Queue genericQueue(){
        return QueueBuilder.durable(GENERIC_QUEUE).deadLetterExchange(DEAD_EXCHANGE).deadLetterRoutingKey("dead.demo").build();
    }

    @Bean
    public Binding genericBinding(Queue genericQueue, Exchange genericExchange){
        return BindingBuilder.bind(genericQueue).to(genericExchange).with(GENERIC_ROUTING_KEY).noargs();
    }


    @Bean
    public Exchange deadExchange(){
        return ExchangeBuilder.topicExchange(DEAD_EXCHANGE).build();
    }

    @Bean
    public Queue deadQueue(){
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    @Bean
    public Binding deadBinding(Queue deadQueue, Exchange deadExchange){
        return BindingBuilder.bind(deadQueue).to(deadExchange).with(DEAD_ROUTING_KEY).noargs();
    }
}

生产者端

package com.kkarma.deadletter;


import com.kkarma.config.DeadLettingConfig;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;

@SpringBootTest
public class Publisher {

    private final RabbitTemplate rabbitTemplate;

    @Autowired
    public Publisher(RabbitTemplate rabbitTemplate){
        this.rabbitTemplate =  rabbitTemplate;
    }


    /**
     * 消费者拒绝消费,消息进入死信队列
     */
    @Test
    public void push() throws IOException {
        rabbitTemplate.convertAndSend(DeadLettingConfig.GENERIC_EXCHANGE, "generic.demo", "正常消息被拒绝消息会变成死信...");
        System.out.println("消息发送成功...");
        System.in.read();
    }

}

消费者端

package com.kkarma.deadletter;

import com.kkarma.config.DeadLettingConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Consumer {

    @RabbitListener(queues = DeadLettingConfig.GENERIC_QUEUE)
    public void pullAndNack(String msg, Channel channel, Message message) throws Exception {
        System.out.println("接收generic-queue队列中的消息内容: " + msg);
        String correlationId = message.getMessageProperties().getCorrelationId();
        System.out.println("唯一标识: " + correlationId);
        // 拒绝消费消息
         channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
        // 确认未成功消费
        // channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
    }
}

测试结果
在这里插入图片描述

6.2 消息指定过期时间

主要是生产者端在发送消息是指定消息的过期时间

    /**
     * 消息指定过期时间
     */
    @Test
    public void publishMsgExpire(){
        String msg = "generic letter expire become dear letter";
        rabbitTemplate.convertAndSend(DeadLettingConfig.GENERIC_EXCHANGE, "generic.demo", msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("10000");
                return message;
            }
        });
    }

6.3 给队列设置消息过期时间

这一项设置主要是通过DeadLettingConfig配置类中的正常队列来进行设置

    @Bean
    public Queue genericQueue(){
        // 这里需要指定当消息变成死信时绑定的死信交换机,并且需要重新指定指定死信交换机和死信队列之间的路由
        return QueueBuilder
                .durable(GENERIC_QUEUE)
                .deadLetterExchange(DEAD_EXCHANGE)
                .deadLetterRoutingKey("dead.demo")
                // 设置队列中消息的过期时间
                .ttl(10000)
                .build();
    }

6.4 设置队列的最大长度

    @Bean
    public Queue genericQueue(){
        // 这里需要指定当消息变成死信时绑定的死信交换机,并且需要重新指定指定死信交换机和死信队列之间的路由
        return QueueBuilder
                .durable(GENERIC_QUEUE)
                .deadLetterExchange(DEAD_EXCHANGE)
                .deadLetterRoutingKey("dead.demo")
                // 设置队列最大长度,当队列已满,再路由过来的消息就被当成死信处理
                .maxlength(10)
                .build();
    }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值