22-07-18 西安 RabbitMQ(02)spring整合RabbitMQ、SpringBoot整合RabbitMQ

“芜湖!那岂不是说,此次有幸目睹圣女的风采了”

“在下李兆鸣实名喜欢神若圣女!!!”

“哎哎哎,说话那小子你TM谁啊,老子才叫李兆鸣,你别给老子树敌啊草!


Spring 整合RabbitMQ(不推荐看)

1、消费者工程和生产者工程

1、创建工程spring-rabbitmq-producer和spring-rabbitmq-consumer,引入依赖和插件

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>2.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
    </dependencies>
    
     <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、生产者端创建 spring-rabbitmq.xml 整合配置文件,内容如下

指定要连接的MQ服务器,因为是简单模式,就只是创建队列(指定队列名)就好了

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="192.168.2.108"
                               port="5672"
                               username="admin"
                               password=""
                               virtual-host="MyVirtualHost"/>

    <!--定义admin组件管理(创建)交换机、队列-->
    <rabbit:admin connection-factory="connectionFactory"/>

    <!--定义持久化队列,不存在则自动创建;不绑定到交换机则绑定到默认交换机
    默认交换机类型为direct,名字为:"",路由键为队列的名称
    -->
    <!--简单模式,没有交换机,1个队列
      name="队列的名字"
      id: 队列bean对象的唯一表示
    -->
    <rabbit:queue id="spring_simple_queue" name="spring_simple_queue" durable="true"
                  auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>

    <!--定义rabbitTemplate对象(模板对象,封装了大量的API操作)操作可以在代码中方便发送消息-->
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
</beans>

生产端创建一个测试类:

convert :直译为“转换”

convertAndSend()方法的3个参数分别指:

参数1:交换机名称 

参数2:路由键名(广播设置为空) 

参数3:发送的消息内容

@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class ProducerTest {
    @Autowired
    RabbitTemplate rabbitTemplate;

    @Test
    public void t1() {
        String message = "简单模式: hello spring rabbitmq";
        //简单模式 路由键与队列同名
        rabbitTemplate.convertAndSend("", "spring_simple_queue", message);
    }
}

运行生产端测试方法t1,在rabbitMq管理控制台中可以看到


消费端创建spring-rabbitmq.xml 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <!-- 定义rabbitmq connectionFactory -->
    <rabbit:connection-factory id="connectionFactory" host="192.168.2.108"
                               port="5672"
                               username="admin"
                               password="123456"
                               virtual-host="MyVirtualHost"/>

    <bean id="springQueueListener" class="com.atguigu.rabbitmq.listener.SpringQueueListener"/>

    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="springQueueListener" queue-names="spring_simple_queue"/>
    </rabbit:listener-container>
</beans>

2、消费端队列监听器

在配置文件中看的出来消费端出现一个很重要的东西 队列监听器

干什么作用的呢,就是在消费端在消费某一个队列的消息时会和一个监听器匹配,消息的内容可由该监听器拿到

public class SpringQueueListener implements MessageListener {
    @Override
    public void onMessage(Message message) {
        try {
            String msg = new String(message.getBody(), "utf-8");

            System.out.printf("接收路由名称为:%s,路由键为:%s,队列名为:%s的消息:%s \n",
                    message.getMessageProperties().getReceivedExchange(),
                    message.getMessageProperties().getReceivedRoutingKey(),
                    message.getMessageProperties().getConsumerQueue(),
                    msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

消费端创建测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath: spring-rabbitmq.xml")
public class ConsumerTest {

    @Test
    public void test1(){
        boolean flag = true;
        //作为消费方,服务不停
        while (flag){

        }
    }
}

 消费端接收到消息,控制台输出


3、Spring整合-MQ工作模式

生产者端在spring-rabbitmq.xml 加入内容

    <!--工作模式 1个队列-->
    <rabbit:queue id="spring_work_queue" name="spring_work_queue" durable="true"
                  auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>

在测试类ProducerTest里加测试方法。

convertAndSend()方法的3个参数分别指:
/**
 * 参数1:交换机名称
 * 参数2:路由键名(广播设置为空)
 * 参数3:发送的消息内容
 */
    @Test
    public void t2() {
        for (int i = 0; i < 10; i++) {
            String message = "工作模式:hello spring rabbitmq---"+i;
            rabbitTemplate.convertAndSend("", "spring_work_queue", message);
        }
    }

在消费者端就更简单了 ,只需要在spring-rabbitmq.xml中配置监听器和队列名的绑定关系。在工作模式里,就是多个消费者监听同一个队列,配置如下:

测试结果


4、Spring整合-其余模式

剩下的3种模式就一起说了吧

convertAndSend()方法的3个参数分别指:
/** * 参数1:交换机名称 * 参数2:路由键名(广播设置为空) * 参数3:发送的消息内容 */

由于这个方法需要的参数,我们可以推论出在,这三种模式下都是需要指定交换机的名字,类型。并且队列和交换机之间是有不同的绑定关系的。定向交换机(路由模式)就用了key作为路由key。而通配符交换机使用pattern作为路由key。

    <!--广播交换机  fanout  一个交换机两个队列   队列和交换机之间的关系  : 绑定  -->

    <rabbit:queue id="spring_fanout_queue1" name="spring_fanout_queue1" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>
    <rabbit:queue id="spring_fanout_queue2" name="spring_fanout_queue2" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>

    <rabbit:fanout-exchange name="spring_fanout_ex" id="fanoutEx" auto-declare="true" auto-delete="false" durable="true">
        <!--绑定-->
        <rabbit:bindings>
            <rabbit:binding queue="spring_fanout_queue1"></rabbit:binding>
            <rabbit:binding queue="spring_fanout_queue2"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <!--定向交换机  direct  一个交换机 两个队列   有绑定关系,且指定绑定条件 routingkey-->
    <rabbit:queue id="spring_direct_queue1" name="spring_direct_queue1" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>
    <rabbit:queue id="spring_direct_queue2" name="spring_direct_queue2" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>
    <rabbit:direct-exchange name="spring_direct_ex" durable="true" auto-declare="true" auto-delete="false">
        <rabbit:bindings>
            <rabbit:binding queue="spring_direct_queue1" key="springmq"></rabbit:binding>
            <rabbit:binding queue="spring_direct_queue2" key="springmq123"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:direct-exchange>

    <!-- topic 通配符交换机  一个交换机两个队列   实现绑定关系  且 指定条件   注意: 条件支持通配符  # *-->

    <rabbit:queue id="spring_topic_queue1" name="spring_topic_queue1" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>
    <rabbit:queue id="spring_topic_queue2" name="spring_topic_queue2" durable="true" auto-delete="false" auto-declare="true" exclusive="false"></rabbit:queue>
    <rabbit:topic-exchange name="spring_topic_ex" durable="true" auto-declare="true" auto-delete="false">
        <rabbit:bindings>
            <!--pattern 路由key -->
            <rabbit:binding pattern="topic.*" queue="spring_topic_queue1"></rabbit:binding>
            <rabbit:binding pattern="topic.#" queue="spring_topic_queue2"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:topic-exchange>

对应的测试方法里

    @Test
    public void t3() {
        String message = "发布订阅模式(广播): hello spring rabbitmq";
        /**
         * 参数1:交换机名称
         * 参数2:路由键名(广播设置为空)
         * 参数3:发送的消息内容
         */
        rabbitTemplate.convertAndSend("spring_fanout_ex", "", message);
    }

    @Test
    public void t4() {
        String message = "路由模式: hello spring rabbitmq";
        rabbitTemplate.convertAndSend("spring_direct_ex", "springmq", message);
    }

    @Test
    public void t5() {
        /**
         * 参数1:交换机名称
         * 参数2:路由键名
         * 参数3:发送的消息内容
         */
        String message = "通配符模式: hello spring rabbitmq";
        rabbitTemplate.convertAndSend("spring_topic_ex", "topic.a", message);
    }

在消费端,就不写了。累了,毁灭吧


SpringBoot整合RabbitMQ(重要)

消息生产者工程

1、生产者工程依赖、配置文件

创建工程producer-springboot并引入依赖

    <!--
1. 父工程依赖
-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>

    <dependencies>
        <!--2. rabbitmq-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

创建配置文件application.yml

spring:
  rabbitmq:
    host: 192.168.2.108
    port: 5672
    virtual-host: /xiaoyumao
    username: xiaoyumao
    password: xiaoyumao
    #生产者参数
    #开启生产者确认回调
    publisher-confirm-type: simple # SIMPLE-同步确认(阻塞) CORRELATED-异步确认
    publisher-returns: true # 确认消息是否到达队列

2、配置类RabbitMQConfig

在配置类中使用@Bean的方式配置创建交换机、队列,绑定关系

@Configuration
public class RabbitMQConfig {
    public static final String EXCHANGE_NAME = "boot_ex";
    public static final String QUEUE_NAME = "boot_queue";

    //1.创建交换机
    @Bean("bootExchange")
    public Exchange bootExchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE_NAME) //交换机名
                .durable(false)//是否持久化
                .ignoreDeclarationExceptions()//是否忽略重复定义声明该交换机的异常
                .build();
    }

    //2.创建队列
    @Bean("bootQueue")
    public Queue bootQueue() {
        return QueueBuilder
                .nonDurable(QUEUE_NAME) //不持久化并指定队列名称
                .build();
    }

    //3. 队列和交换机绑定关系 Binding
    @Bean
    public Binding bindQueueExchange(@Qualifier("bootQueue") Queue bootQueue,
                                     @Qualifier("bootExchange") Exchange bootExchange) {
        return BindingBuilder
                .bind(bootQueue)
                .to(bootExchange)
                .with("boot.#")//路由key
                .noargs();//noargs():表示不指定参数
    }
}

bindQueueExchange方法参数入参:默认使用autoWired装配。(先类型再名字),但是队列不可能只有一个

其实只要注意,还是可以不用写@Bean()里的value属性,也可以不写@Qualifier的,还保证错不了,就是直接把参数名直接复制过来。。省力还错不了


3、生产者投递消息

生产端直接注入RabbitTemplate完成消息发送,

是因为 springboot自动配置,会给RabbitTemplate初始化并注入容器中。。

@SpringBootTest(classes = RabbitMQApplication.class) //为测试提供上下文环境,class指定启动类
public class MqTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;//发送mq消息的客户端

    @Test //声明需测试的方法
    public void testSend(){
        //参数1:交换机名称  参数2:路由key  参数3:消息内容
        rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_NAME,"boot.boot","mq hello");
    }
}

1.生产者发送消息不关心发送给哪个队列,但是发送消息前必须先初始化交换机,队列,和他们的绑定关系。

2.生产者发消息的时候路由key是写死的,配置类RabbitMQConfig队列绑定交换机的时候才使用通配符。


4、生产者—确认回调配置

@PostConstruct注解的方法,在对象加载完依赖注入后执行。通常都是一些初始化的操作,但初始化可能依赖于注入的其他组件,所以要等依赖全部加载完

继续在RabbitMQConfig这个配置类里,添加生产者确认回调配置,内容如下:

@Configuration
public class RabbitMQConfig {
    @Autowired
    RabbitTemplate rabbitTemplate;
    //生产者确认回调配置
    @PostConstruct //标注的方法会在构造器执行后立即调用一次
    public void init(){
        rabbitTemplate.setConfirmCallback((correlationData,ack,cause)->{
            // ack:  true代表消息到达交换机,false代表消息没有到达交换机
            System.out.println("ConfirmCallback ack: " + ack );
            System.out.println("ConfirmCallback cause: " + cause);
        });
      /*
        消息没有到达队列时回调:
     */
        rabbitTemplate.setReturnCallback((message,replyCode,replyText, exchange,routingKey)->{
            System.out.println("ReturnCallback replyCode: "+ replyCode);
            System.out.println("ReturnCallback replyText: "+ replyText);
            System.out.println("ReturnCallback exchange: "+ exchange);
            System.out.println("ReturnCallback routingKey: "+ routingKey);
        /*
        消息没有到达队列 重新投递消息,再次失败的消息可以存到日志中  或者redis缓存
         */
        });
    }
}

消息消费者工程

1、消费者工程依赖、配置文件

创建消费者工程consumber-springboot,引入依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
    </parent>
    <dependencies>
        <!--RabbitMQ 启动依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

创建application.yml

spring:
  rabbitmq:
    host: 192.168.2.108
    port: 5672
    virtual-host: /xiaoyumao
    username: xiaoyumao
    password: xiaoyumao
    listener:
      type: simple # simple-listener容器使用一个额外线程处理消息  direct-listener(监听器)容器直接使用consumer线程
      simple:
        acknowledge-mode: manual # manual-手动  auto-自动(无异常直接确认,有异常无限重试) none-不重试
        prefetch: 1 # 配置的值代表一次接收消息的数量
        concurrency: 3 # 避免消息堆积,初始化多个消费者线程

2、创建监听器 @RabbitListener

消费端直接使用 @RabbitListener 完成消息接收

@RabbitListener标注的一个方法就是mq的一个消费者

import com.rabbitmq.client.Channel;
@Component
public class RabbitmqListener {
    @RabbitListener(queues = { //消费已存在的队列 中的消息
            "boot_queue"
    })
    public void listener(Message message , String msg , Channel channel) {
            System.out.println(msg);
    }
}

启动主程序类MqConsumerApplication

@SpringBootApplication
public class MqConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(MqConsumerApplication.class, args);
    }
}

控制台就可以自动消费boot_queue这个队列的消息了

但是由于我们在配置文件配置的手动确认,所以队列boot_queue中消息还在,由ready变为unacked。


3、消费消息过程发生异常?

默认情况(auto)下,消息者消费消息过程发生异常,怎么处理

spring.rabbitmq.listener.simple.acknowledge-mode=auto

消息消费成功自动ack,

如果有异常消费失败不会ack,但是会无限重试确认消费消息

如果消费异常的消费者停止,消息又变成可以消费的状态

处理异常的正确的做法是acknowledge-mode=manual

1.配置文件中设置为手动确认消息

spring.rabbitmq.listener.simple.acknowledge-mode=manual

2.合理使用try...catch...

@Component
public class RabbitmqListener {
    @RabbitListener(queues = { //消费已存在的队列 中的消息
            "boot_queue"
    })
    public void listener(Message message , String msg , Channel channel) throws IOException {
        try {
            System.out.println(msg);
            int i = 1/0;
            //手动确认消息   参数2:不批量确认消息的消费
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            e.printStackTrace();
            if(message.getMessageProperties().isRedelivered()){
                //如果消息重复消费后仍消费失败  丢弃消息
                //如果没有绑定死信队列 消息直接丢弃,如果绑定了死信队列 消息会被路由到死信交换机,进而丢弃到死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else{
                //业务执行有异常 消息没能正常消费
                //第一次消费出现异常可以拒绝确认消息的消费:让消息重新归队
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false , true);
            }
        }
    }
}

4、注解创建:交换机、队列和绑定关系

方式一:通过配置类创建交换机、队列和他们的绑定关系,上面在生产者工程中RabbitMQConfig已经用到了,不再说。

方式二:通过注解创建交换机、队列和他们的绑定关系

    //通过注解创建交换机队列 绑定
    @RabbitListener(bindings = {
            @QueueBinding(
                    exchange = @Exchange(
                            name = "test2.exchange", //交换机名
                            durable = "true", //持久化
                            type = ExchangeTypes.TOPIC, //类型
                            ignoreDeclarationExceptions = "true" //异常忽略
                    ),
                    value = @Queue(
                            name = "test2.queue",
                            durable = "true"
                    ),
                    key = "test2.#"  //路由key
            )
    })
    public void listener(Message message , String msg , Channel channel) throws IOException {
        try {
            System.out.println("接收到消息: "+msg);
            System.out.println(new String(message.getBody()));
            //手动确认消息   参数2:不批量确认消息的消费
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            e.printStackTrace();
            if(message.getMessageProperties().isRedelivered()){
                //如果消息重复消费后仍消费失败  丢弃消息
                //如果没有绑定死信队列 消息直接丢弃,如果绑定了死信队列 消息会被路由到死信交换机,进而丢弃到死信队列
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else{
                //业务执行有异常 消息没能正常消费
                //第一次消费出现异常可以拒绝确认消息的消费:让消息重新归队
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),false , true);
            }
        }
    }

@RabbitListener:方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:

  • bindings:指定绑定关系,可以有多个。值是@QueueBinding的数组。

  • @QueueBinding包含下面属性:

    • value:这个消费者关联的队列。值是@Queue,代表一个队列

    • exchange:队列所绑定的交换机,值是@Exchange类型

    • key:队列和交换机绑定的RoutingKey


死信队列和延迟队列

1、死信队列

死信队列:如果队列里的消息出现以下情况,那么该消息将成为“死信”。

  1. 消息被否定确认,使用 channel.basicNack 或 channel.basicReject ,并且此时requeue 属性被设置为false。
  2. 消息在队列的存活时间超过设置的TTL时间。
  3. 消息队列的消息数量已经超过最大队列长度。

“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列。那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。


2、延迟队列

如果队列不设置TTL,表示消息永远不会过期

如果将TTL设置为0,则表示除非此时可以直接投递该消息到消费者,否则该消息将会被丢弃。

如果设置了队列的TTL属性,那么一旦消息过期,就会被队列丢弃

一般死信队列和延时队列一起使用,使用场景

  1. 订单在十分钟之内未支付则自动取消。

  2. 新创建的店铺,如果在十天内都没有上传过商品,则自动发送消息提醒。

  3. 账单在一周内未支付,则自动结算。

  4. 用户注册成功后,如果三天内没有登陆则进行短信提醒。

  5. 用户发起退款,如果三天内没有得到处理则通知相关运营人员。

  6. 预定会议后,需要在预定的时间点前十分钟通知各个与会人员参加会议。


3、创建业务队列、死信队列、延迟队列

咱一块一块看。。提前声明:以下各部分代码均写在该配置类RabbitmqConfig 

@Configuration
public class RabbitmqConfig {

}

第一块:业务队列、业务交换机部分

    //业务队列业务交换机绑定:业务队列绑定死信队列(业务队列消息以后丢弃时会路由到死信队列)
    @Bean
    public Exchange test3Exchange(){
        return ExchangeBuilder.topicExchange("test3.exchange")
                .ignoreDeclarationExceptions()
                .durable(true)
                .build();
    }

    @Bean
    public Queue test3Queue(){
        return QueueBuilder.durable("test3.queue")//持久化并指定队列名称
                .deadLetterExchange("dead.exchange")
                .deadLetterRoutingKey("dead.msg")
                .build();
    }

    @Bean
    public Binding test3Binding(Exchange test3Exchange,Queue test3Queue){
        return BindingBuilder.bind(test3Queue)
                .to(test3Exchange)
                .with("test3.*")
                .noargs();
    }

看点在于创建业务队列 test3Queue()时,

-----------------------------------

第二块:死信队列、死信交换机部分

    //死信队列死信交换机 绑定
    @Bean
    public Exchange deadExchange(){
        return ExchangeBuilder.topicExchange("dead.exchange")
                .ignoreDeclarationExceptions()
                .durable(true)
                .build();
    }

    @Bean
    public Queue deadQueue(){
        return QueueBuilder.durable("dead.queue")
//                .expires()  队列未使用被删除的时间
                //=====绑定延迟队列
                .ttl(15000) //消息未被消费的存活时间 超过时长消息会被丢弃
                .deadLetterExchange("delay.exchange")
                .deadLetterRoutingKey("delay.msg")
                .build();
    }

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

看点在于,延迟交换机就是死信交换机,也是使用deadLetterExchange

-------------------------------------

第三块:延迟队列、延迟交换机部分

    //延迟队列延迟交换机 绑定
    @Bean
    public Exchange delayExchange(){
        return ExchangeBuilder.topicExchange("delay.exchange")
                .ignoreDeclarationExceptions()
                .durable(true)
                .build();
    }

    @Bean
    public Queue delayQueue(){
        return QueueBuilder.durable("delay.queue")
                .build();
    }

    @Bean
    public Binding delayBinding(Exchange delayExchange,Queue delayQueue){
        return BindingBuilder.bind(delayQueue)
                .to(delayExchange)
                .with("delay.msg")
                .noargs();
    }

3、死信队列和延迟队列测试

1.在消息的消费者工程里: 故意出现异常2/0

@RabbitListener(queues = {
        "test3.queue"
})
public void test3Listener(Message message , Map map , Channel channel) throws IOException {
    try {
        System.out.println(map);
        int i = 2/0;
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    } catch (Exception e) {
        e.printStackTrace();
        if(message.getMessageProperties().isRedelivered()){
            channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
        }else{
            channel.basicNack(message.getMessageProperties().getDeliveryTag(),
                    false,true);
        }
    }
}

2.在生产者工程发送如下消息

Map map = new HashMap<>();
map.put("spuid","123456789");
map.put("operate","1");
//参数1:交换机名称  参数2:路由key   参数3:消息内容
rabbitTemplate.convertAndSend("test3.exchange", "test3.hehe" , map);

3.查看现象

观察消费者服务控制台,抛出两次异常(channel.basicNack重试)后消息会进入到死信队列,15秒后消息就到了延迟队列。

D:消息持久化

TTL:消息过期时间

DLX:死信交换机

DLK:死信路由

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值