SpringBoot整合RabbitMQ与RabbitMQ高级特性

SpringBoot整合RabbitMQ与RabbitMQ高级特性

  • Spring整合RabbitMQ
    • 生产者
    • 消费者
    • 测试简单模式
    • 测试fanout广播模式
    • 小结
  • SpringBoot整合RabbitMQ
    • 生产端
    • 消费端
    • 案例测试
    • 小结
  • 消息的可靠性投递
    • 生产端
    • 案例演示
    • 生产方确认Confirm小结
    • Consumer Ack
    • Consumer Ack案例演示
    • Consumer Ack 小结
    • 消息可靠性总结
  • 消费端限流
    • 消费端限流案例
    • 消费端限流小结

Spring整合RabbitMQ

生产者

  • 创建生产者工程
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第1张图片

  • 添加依赖

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-contextartifactId>
    <version>5.1.7.RELEASEversion>
dependency>

<dependency>
    <groupId>org.springframework.amqpgroupId>
    <artifactId>spring-rabbitartifactId>
    <version>2.1.8.RELEASEversion>
dependency>

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.12version>
dependency>

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-testartifactId>
    <version>5.1.7.RELEASEversion>
dependency>
  • 配置整合
    rabbitmq.properties
rabbitmq.host=192.168.1.104
rabbitmq.port=5672
rabbitmq.username=yemuxia
rabbitmq.password=123456
rabbitmq.virtual-host=/yemuxia_vir

spring-rabbitmq-producer.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>
    
    <rabbit:admin connection-factory="connectionFactory"/>

    
    

    <rabbit:queue id="spring_queue" name="spring_queue"    auto-declare="true"/>

    
    
    <rabbit:queue id="spring_fanout_queue_1" name="spring_fanout_queue_1" auto-declare="true"/>

    
    <rabbit:queue id="spring_fanout_queue_2" name="spring_fanout_queue_2" auto-declare="true"/>

    
    <rabbit:fanout-exchange id="spring_fanout_exchange" name="spring_fanout_exchange"  auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding  queue="spring_fanout_queue_1"  />
            <rabbit:binding queue="spring_fanout_queue_2"/>
        rabbit:bindings>
    rabbit:fanout-exchange>


    
    <rabbit:queue id="spring_direct_queue" name="spring_direct_queue"  auto-declare="true"/>

    
    <rabbit:direct-exchange name="spring_direct_exchange" >
        <rabbit:bindings>
            
            <rabbit:binding queue="spring_direct_queue" key="info">rabbit:binding>
        rabbit:bindings>

    rabbit:direct-exchange>

    
    
    <rabbit:queue id="spring_topic_queue_star" name="spring_topic_queue_star"  auto-declare="true"/>
    
    <rabbit:queue id="spring_topic_queue_well" name="spring_topic_queue_well" auto-declare="true"/>
    
    <rabbit:queue id="spring_topic_queue_well2" name="spring_topic_queue_well2" auto-declare="true"/>

    
    <rabbit:topic-exchange id="spring_topic_exchange"  name="spring_topic_exchange" auto-declare="true">
        <rabbit:bindings>
            <rabbit:binding pattern="baiqi.*"  queue="spring_topic_queue_star"/>
            <rabbit:binding pattern="baiqi.#" queue="spring_topic_queue_well"/>
            <rabbit:binding pattern="itcast.#" queue="spring_topic_queue_well2"/>
        rabbit:bindings>
    rabbit:topic-exchange>

    
    <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>
beans>
  • 编写代码发送消息
    添加单元测试类ProducerTest
package com.baiqi;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

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

    //1.注入 RabbitTemplate
    @Autowired
    private RabbitTemplate rabbitTemplate;


    @Test
    public void testHelloWorld(){
     
        //2.发送消息

        rabbitTemplate.convertAndSend("spring_queue","hello world spring....");
    }


    /**
     * 发送fanout消息
     */
    @Test
    public void testFanout(){
     
        //2.发送消息

        rabbitTemplate.convertAndSend("spring_fanout_exchange","","spring fanout....");
    }


    @Test
    public void testDirect(){
     
        //2.发送消息

        rabbitTemplate.convertAndSend("spring_direct_exchange","info","spring Direct....");
    }

    /**
     * 发送topic消息
     */
    @Test
    public void testTopics(){
     
        //2.发送消息

        rabbitTemplate.convertAndSend("spring_topic_exchange","baiqi.hehe.haha","spring topic....");
    }
}

消费者

  • 创建消费者01工程
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第2张图片

  • 添加依赖(和生产者项目依赖保持一致)

  • 配置整合

rabbitmq.host=192.168.1.104
rabbitmq.port=5672
rabbitmq.username=yemuxia
rabbitmq.password=123456
rabbitmq.virtual-host=/yemuxia_vir

spring-rabbitmq-consumer.xml


<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="springQueueListener" class="com.baiqi.rabbitmq.listener.SpringQueueListener"/>
    <bean id="fanoutListener1" class="com.baiqi.rabbitmq.listener.FanoutListener"/>


    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
       <rabbit:listener ref="springQueueListener" queue-names="spring_queue"/>
        <rabbit:listener ref="fanoutListener1" queue-names="spring_fanout_queue_1"/>
        
    rabbit:listener-container>
beans>
  • 编写消息监听器
package com.baiqi.rabbitmq.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class SpringQueueListener implements MessageListener {
     
    @Override
    public void onMessage(Message message) {
     
        //打印消息
        System.out.println(new String(message.getBody()));
    }
}

package com.baiqi.rabbitmq.listener;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;

public class FanoutListener implements MessageListener {
     
    @Override
    public void onMessage(Message message) {
     
        //打印消息
        System.out.println(new String(message.getBody()));
    }
}

添加此类是为了启动消费者服务

package com.baiqi.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test {
     

    public static void main(String[] args) {
     
        //初始化IOC容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring-rabbitmq-consumer.xml");


    }
}

测试简单模式

  • 运行消费者,再运行生产者testHelloWorld单元用例
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第3张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第4张图片

测试fanout广播模式

  • 创建消费者02项目
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第5张图片
  • 配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/rabbit
       http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
    
    <context:property-placeholder location="classpath:rabbitmq.properties"/>

    
    <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                               port="${rabbitmq.port}"
                               username="${rabbitmq.username}"
                               password="${rabbitmq.password}"
                               virtual-host="${rabbitmq.virtual-host}"/>

    <bean id="fanoutListener2" class="com.baiqi.rabbitmq.listener.FanoutListener2"/>
    
    <rabbit:listener-container connection-factory="connectionFactory" auto-declare="true">
        <rabbit:listener ref="fanoutListener2" queue-names="spring_fanout_queue_2"/>
        
    rabbit:listener-container>
beans>
  • 启动消费者01和02项目,并启动生产者testFanout单元测试
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第6张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第7张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第8张图片

小结

  • 使用 Spring 整合 RabbitMQ 将组件全部使用配置方式实现,简化编码
  • Spring 提供 RabbitTemplate 简化发送消息 API
  • 使用监听机制简化消费者编码

SpringBoot整合RabbitMQ

生产端

  • 创建生产者SpringBoot工程
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第9张图片

  • 引入start,依赖坐标

<parent>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-parentartifactId>
    <version>2.1.4.RELEASEversion>
parent>

<dependencies>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-amqpartifactId>
    dependency>
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
    dependency>
dependencies>
  • 编写yml配置,基本信息配置
# 配置RabbitMQ的基本信息  ip 端口 username  password..
spring:
  rabbitmq:
    host: 192.168.1.104 #主机ip
    port: 5672 #端口
    username: yemuxia
    password: 123456
    virtual-host: /yemuxia_vir
  • 定义交换机,队列以及绑定关系的配置类
package com.baiqi.rabbitmq.config;

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


@Configuration
public class RabbitMqConfig {
     

    //定义交换机的名字
    public static final String  EXCHANGE_NAME = "boot_topic_exchange";
    //定义队列的名字
    public static final String QUEUE_NAME = "boot_queue";

    //1、声明交换机
    @Bean("bootExchange")
    public Exchange bootExchange(){
     

        return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
    }



    //2、声明队列
    @Bean("bootQueue")
    public Queue bootQueue(){
     

        return QueueBuilder.durable(QUEUE_NAME).build();
    }


    //3、队列与交换机进行绑定
    @Bean
    public Binding bindQueueExchange(@Qualifier("bootQueue") Queue queue, @Qualifier("bootExchange") Exchange exchange){
     
        return BindingBuilder.bind(queue).to(exchange).with("boot.#").noargs();
    }

}

  • 注入RabbitTemplate,调用方法,完成消息发送
package com.baiqi.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;


@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
     

     //注入 RabbitTemplate
    @Autowired
     private RabbitTemplate  rabbitTemplate;

     @Test
     public void send(){
     
         rabbitTemplate.convertAndSend("boot_topic_exchange","boot.haha","boot mq...");
     }
}

消费端

  • 创建消费者SpringBoot工程
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第10张图片

  • 引入start,依赖坐标(和生产端保持一致)

  • 编写yml配置,基本信息配置(和生产端保持一致)

  • 定义监听类,使用@RabbitListener注解完成队列监听。

package com.baiqi.consumerspringboot;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;


@Component
public class RabbitMQListener {
     

      //定义方法进行信息的监听   RabbitListener中的参数用于表示监听的是哪一个队列
      @RabbitListener(queues = "boot_queue")
      public void ListenerQueue(Message message){
     
          System.out.println("message:"+new String(message.getBody()));
      }
}


案例测试

运行消费端服务,再运行生产端的单元测试案例
SpringBoot整合RabbitMQ与RabbitMQ高级特性_第11张图片

小结

  • SpringBoot提供了快速整合RabbitMQ的方式
  • 基本信息在yml中配置,队列交互机以及绑定关系在配置类中使用Bean的方式配置
  • 生产端直接注入RabbitTemplate完成消息发送
  • 消费端直接使用@RabbitListener完成消息接收

消息的可靠性投递

生产端

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

  • confirm 确认模式
  • return 退回模式

rabbitmq 整个消息投递的路径为:
producer—>rabbitmq broker—>exchange—>queue—>consumer

  • 消息从 producer 到 exchange 则会返回一个 confirmCallback 。
  • 消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。

我们将利用这两个 callback 控制消息的可靠性投递

案例演示

  • 创建生产端项目,并配置
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第12张图片
 
 <context:property-placeholder location="classpath:rabbitmq.properties"/>

 
 <rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                            port="${rabbitmq.port}"
                            username="${rabbitmq.username}"
                            password="${rabbitmq.password}"
                            virtual-host="${rabbitmq.virtual-host}"
                            publisher-confirms="true"
                            publisher-returns="true"
 />
 
 <rabbit:admin connection-factory="connectionFactory"/>

 
 <rabbit:template id="rabbitTemplate" connection-factory="connectionFactory"/>




 
 <rabbit:queue id="test_queue_confirm" name="test_queue_confirm">rabbit:queue>

 <rabbit:direct-exchange name="test_exchange_confirm">
     <rabbit:bindings>
         <rabbit:binding queue="test_queue_confirm" key="confirm">rabbit:binding>
     rabbit:bindings>
 rabbit:direct-exchange>
package com.baiqi.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @author 白起老师
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq-producer.xml")
public class ProducerTest {
     

        @Autowired
        private RabbitTemplate  rabbitTemplate;


        //测试   Confirm 模式
    @Test
    public void testConfirm() {
     

         //定义回调
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
     
            /**
             *
             * @param correlationData 相关配置信息
             * @param ack   exchange交换机 是否成功收到了消息。true 成功,false代表失败
             * @param cause 失败原因
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
     
                System.out.println("confirm方法被执行了....");

                 //ack 为  true表示 消息已经到达交换机
                if (ack) {
     
                    //接收成功
                    System.out.println("接收成功消息" + cause);
                } else {
     
                    //接收失败
                    System.out.println("接收失败消息" + cause);
                    //做一些处理,让消息再次发送。
                }
            }
        });

           //进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message Confirm...");

        //进行睡眠操作
        try {
     
            Thread.sleep(5000);
        } catch (Exception e) {
     
            e.printStackTrace();
        }
    }


    //测试 return模式
    @Test
    public void testReturn() {
     

        //设置交换机处理失败消息的模式   为true的时候,消息达到不了 队列时,会将消息重新返回给生产者
        rabbitTemplate.setMandatory(true);

        //定义回调
        rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
     
            /**
             *
             * @param message   消息对象
             * @param replyCode 错误码
             * @param replyText 错误信息
             * @param exchange  交换机
             * @param routingKey 路由键
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
     
                System.out.println("return 执行了....");

                System.out.println("message:"+message);
                System.out.println("replyCode:"+replyCode);
                System.out.println("replyText:"+replyText);
                System.out.println("exchange:"+exchange);
                System.out.println("routingKey:"+routingKey);

                //处理
            }
        });
        //进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm","message return...");

        //进行睡眠操作
        try {
     
            Thread.sleep(5000);
        } catch (Exception e) {
     
            e.printStackTrace();
        }
    }
}

  • 创建消费端项目,并配置
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第13张图片

<context:property-placeholder location="classpath:rabbitmq.properties"/>


<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}"
                           port="${rabbitmq.port}"
                           username="${rabbitmq.username}"
                           password="${rabbitmq.password}"
                           virtual-host="${rabbitmq.virtual-host}"/>


<context:component-scan base-package="com.baiqi.listener" />

<rabbit:listener-container connection-factory="connectionFactory" >
        <rabbit:listener ref="ackListener" queue-names="test_queue_confirm">rabbit:listener>
rabbit:listener-container>
package com.baiqi.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;


@Component
public class AckListener implements ChannelAwareMessageListener {
     

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
     
        //1、获取消息的id
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
     


        //2、获取消息
        System.out.println("message:"+new String(message.getBody()));

        //3、进行业务处理
        System.out.println("=====进行业务处理====");

        //4、进行消息签收
        channel.basicAck(deliveryTag, true);

        } catch (Exception e) {
     

            //拒绝签收
             /*
            第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
             */
            channel.basicNack(deliveryTag, true, true);

        }
    }
}

  • 测试Confirm 模式
    项目运行演示,先运行生产端,再运行消费端(队列和交换机在哪个项目创建,就先启动哪个服务,本案例队列在生产端创建)
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第14张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第15张图片
   //进行消息发送    rabbitTemplate.convertAndSend("test_exchange_confirm11","confirm","message Confirm...");

在testConfirm单元用例中故意将交换机名称写错,让其走回调的else分支。
SpringBoot整合RabbitMQ与RabbitMQ高级特性_第16张图片
SpringBoot整合RabbitMQ与RabbitMQ高级特性_第17张图片

  • 测试Return 模式
    将单元测试用例的testReturn中部分逻辑修改如下
//进行消息发送
        rabbitTemplate.convertAndSend("test_exchange_confirm","confirm11","message return...");

此处故意将路由名称写错,让交换机不能正确路由到队列中去
运行该案例
SpringBoot整合RabbitMQ与RabbitMQ高级特性_第18张图片

生产方确认Confirm小结

  • 设置ConnectionFactory的publisher-confirms=“true” 开启 确认模式。
  • 使用rabbitTemplate.setConfirmCallback设置回调函数。当消息发送到exchange后回调confirm方法。在方法中判断ack,如果为true,则发送成功,如果为false,则发送失败,需要处理。
  • 设置ConnectionFactory的publisher-returns=“true” 开启 退回模式。
  • 使用rabbitTemplate.setReturnCallback设置退回函数,当消息从exchange路由到queue失败后,如果设置了rabbitTemplate.setMandatory(true)参数,则会将消息退回给producer。并执行回调函数returnedMessage。

Consumer Ack

ack指Acknowledge,确认。 表示消费端收到消息后的确认方式
有三种确认方式:

  • 自动确认:acknowledge=“none”
  • 手动确认:acknowledge=“manual”
  • 根据异常情况确认:acknowledge=“auto”,(这种方式使用麻烦,不作讲解)

其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息

Consumer Ack案例演示

  • 修改原有消费端程序,去掉签收代码,并配置成手动签收
@Component
public class AckListener implements ChannelAwareMessageListener {
     

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
     
        //1、获取消息的id
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
     


        //2、获取消息
        System.out.println("message:"+new String(message.getBody()));

        //3、进行业务处理
        System.out.println("=====进行业务处理====");


        } catch (Exception e) {
     

        }
    }
}
<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual">
    <rabbit:listener ref="ackListener" queue-names="test_queue_confirm">rabbit:listener>

rabbit:listener-container>
  • 生产端程序修改回正常,并运行
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第19张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第20张图片
    SpringBoot整合RabbitMQ与RabbitMQ高级特性_第21张图片
  • 再次修改消费端程序,模拟异常出现
package com.baiqi.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;


@Component
public class AckListener implements ChannelAwareMessageListener {
     

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
     
        //1、获取消息的id
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
     


        //2、获取消息
        System.out.println("message:"+new String(message.getBody()));

        //3、进行业务处理
        System.out.println("=====进行业务处理====");

        //模拟出现异常
        int  i = 5/0;

        //4、进行消息签收
        channel.basicAck(deliveryTag, true);

        } catch (Exception e) {
     

            //拒绝签收
             /*
            第三个参数:requeue:重回队列。如果设置为true,则消息重新回到queue,broker会重新发送该消息给消费端
             */
            channel.basicNack(deliveryTag, true, true);

        }
    }
}

SpringBoot整合RabbitMQ与RabbitMQ高级特性_第22张图片
消费端出现异常之后,拒绝签收并将消息返回至发送端,发送端又开始重发,但是消费端一直再抛异常,导致发送端出现了循环发送消息的情况

Consumer Ack 小结

  • 在rabbit:listener-container标签中设置acknowledge属性,设置ack方式 none:自动确认,manual:手动确认
  • 如果在消费端没有出现异常,则调用channel.basicAck(deliveryTag,false);方法确认签收消息
  • 如果出现异常,则在catch中调用 basicNack或 basicReject,拒绝消息,让MQ重新发送消息。

消息可靠性总结

  • 持久化
    • exchange要持久化
    • queue要持久化
    • message要持久化
  • 生产方确认Confirm
  • 消费方确认Ack
  • Broker高可用

消费端限流

SpringBoot整合RabbitMQ与RabbitMQ高级特性_第23张图片

消费端限流案例

  • 生产端案例
//批量发送消息,让消费者每次拉去指定的数量
@Test
public void  testQos(){
     

    for (int i = 0; i < 10; i++) {
     
        // 发送消息
        rabbitTemplate.convertAndSend("test_exchange_confirm", "confirm", "message confirm....");
    }

}
  • 消费端配置修改

<rabbit:listener-container connection-factory="connectionFactory" acknowledge="manual" prefetch="2">

    <rabbit:listener ref="qosListener" queue-names="test_queue_confirm">rabbit:listener>


rabbit:listener-container>
package com.baiqi.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;


@Component
public class QosListener implements ChannelAwareMessageListener {
     

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
     

          //获取到的消息
        System.out.println(new String(message.getBody()));

        Thread.sleep(1000);

        //处理业务逻辑

        //进行消息的签收
        //channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);

    }
}

在不进行签收的情况下,等到消费端签收了这两条内容之后,发送方会继续发送
SpringBoot整合RabbitMQ与RabbitMQ高级特性_第24张图片

消费端限流小结

  • 在< rabbit:listener-container> 中配置 prefetch属性设置消费端一次拉取多少消息
  • 消费端的确认模式一定为手动确认。acknowledge=“manual”

你可能感兴趣的:(分布式消息队列,rabbitmq,spring,boot,java)