SpringBoot整合RabbitMQ(创建队列、一次性消费、发送消息、路由绑定,监听消费)

主要步骤

  • 创建SpringBoot工程
  • 配置RabbitMQ的基本参数
    host 、virtual host、username、password
  • 通过RabbitTemplate创建RabbitAdmin,通过RabbitAdmin操作RabbitMQ
    RabbitAdmin可以创建队列、交换机、发送数据、主动消费数据
    同时SpringBoot提供了Listener监听器来实现实时消费数据

环境

开发工具:IDEA 2020.1
操作系统:Centos7
RabbitMQ版本:Docker中rabbitmq:management
Docker版本:20.10.9

实际操作

创建SpringBoot工程

  • 使用命令创建SpringBoot工程
mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate -DarchetypeArtifactId="maven-archetype-webapp" -DarchetypeGroupId="org.apache.maven.archetypes" -DarchetypeVersion="1.4"

该命令创建的是传统maven工程,需要手动改造成SpringBoot工程
删除WebApp目录及下属内容,改造为下列文件结构
在这里插入图片描述

  • 添加pom文件中的依赖
    完全改造后内容如下,比对完成即可
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.dean</groupId>
  <artifactId>SpringBoot_RabbitMQ</artifactId>
  <version>1.0-SNAPSHOT</version>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
  </parent>



  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <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>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <finalName>SpringBoot_RabbitMQ</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

主要是<parent>、<properties>、<dependency>节点进行了更改

  • 编写启动类
    在这里插入图片描述

配置RabbitMQ的基本参数

  • resources目录下创建application.yml配置文件
    在这里插入图片描述
  • 编写参数
    在这里插入图片描述

操作RabbitMQ

  • 创建一个SpringBoot的测试用例
    上面的操作中,我们已经在pom文件中添加了SpringBoot Test的依赖了
    现在我们需要src目录下创建一个test目录,在test目录下写一个RabbitTest类进行测试
    在这里插入图片描述
    在这里插入图片描述

  • 创建一个队列
    在这里插入图片描述

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitApplication.class)
public class RabbitTest {
    @Resource
    private RabbitTemplate rabbitTemplate;

    //声明一个队列
    @Test
    public void createQueue() {
        //创建RabbitAdmin 用于操作队列
        RabbitAdmin admin = new RabbitAdmin(rabbitTemplate);
        Queue springQueue = new Queue("SpringQueue");
        admin.declareQueue(springQueue);
    }
}

执行后,可以在RabbitMQ的web界面看到新生成的队列
在这里插入图片描述

  • 向队列发送消息
 @Test
    public void sendMsg() {
        rabbitTemplate.convertAndSend("SpringQueue","Hello Spring Rabbit");
    }
    }

在这里插入图片描述
在这里插入图片描述

  • 消费(一次性消费)
    这个场景就是例如我们上线某聊天软件后,需要得知有多少未读信息,不去拿消息本身
@Test
    public void consumeMsg() {
        rabbitAdmin = new RabbitAdmin(rabbitTemplate);
        //队列的属性 Properties继承HashTable
        Properties properties = rabbitAdmin.getQueueProperties("SpringQueue");
        //由于不知道properties里面都有什么,所以输出看一下
        /**
         * 里面的内容如下:
         * QUEUE_MESSAGE_COUNT
         * QUEUE_CONSUMER_COUNT
         * QUEUE_NAME
         */
        Enumeration<?> names = properties.propertyNames();
        while (names.hasMoreElements()) {
            Object e = names.nextElement();
            System.out.println(e);
        }
        //获取消息总数
        String msg = properties.get("QUEUE_MESSAGE_COUNT").toString();
        System.out.println(msg );
        //清空队列 (也就是说本次是一次性消费)
        rabbitAdmin.purgeQueue("SpringQueue");
    }

在这里插入图片描述
消息已被清空
在这里插入图片描述

  • 路由绑定
@Test
    public void routeBind() {
        rabbitAdmin = new RabbitAdmin(rabbitTemplate);
        //声明队列
        //系统消息接收队列,userId在实际生产中为实际用户id
        Queue sysQueue = new Queue("sys.msg.userId");
        //一对一消息接收队列,sendUserId为实际向用户发送消息的用户id
        Queue userQueue=new Queue("user.msg.sendUserId");
        //声明交换机 Direct
        DirectExchange directEx = new DirectExchange("notice.direct");
        //声明绑定
        Binding sysBind = BindingBuilder.bind(sysQueue).to(directEx).with("sys");
        Binding userBind = BindingBuilder.bind(userQueue).to(directEx).with("user");

        //创建队列
        rabbitAdmin.declareQueue(sysQueue);
        rabbitAdmin.declareQueue(userQueue);
        //创建交换机
        rabbitAdmin.declareExchange(directEx);
        //创建绑定关系
        rabbitAdmin.declareBinding(sysBind);
        rabbitAdmin.declareBinding(userBind);
    }

在这里插入图片描述
成功创建的队列
在这里插入图片描述

成功创建的交换机及绑定关系
在这里插入图片描述

  • 通过路由向队列发送消息
@Test
    public void sendMsg2Route() {
        rabbitTemplate.convertAndSend("notice.direct","sys", "系统通知");
        rabbitTemplate.convertAndSend("notice.direct","sys", "系统通知");
        rabbitTemplate.convertAndSend("notice.direct","user", "用户通知");
    }

在这里插入图片描述
向系统队列发两条消息,向用户队列发一条消息
在这里插入图片描述

RabbitMQ监听消费

  • 首先我们需要一个监听器和一个监听配置
    在这里插入图片描述
  • 监听器SysNoticeListener
@Component
public class SysNoticeListener implements ChannelAwareMessageListener {

    @Override
    public void onMessage(Message message, Channel channel) throws Exception {
        System.out.println(Thread.currentThread().getName()+": 监听到消息");
        Thread.sleep(1000);
    }
}

重点在于实现了ChannelAwareMessageListener类,重写了其方法OnMessage

  • 监听配置类 RabbitConfig
@Configuration
public class RabbitConfig {
    //注入自定义的监听器
    @Resource
    private SysNoticeListener sysNoticeListener;

    @Bean
    public SimpleMessageListenerContainer listenerContainer(ConnectionFactory connectionFactory) {
        //创建监听器工厂
        SimpleRabbitListenerContainerFactory factory=new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);

        //ack机制  自动消费 或 手动消费
        //自动消费:无论消费是否成功都进行确认
        //手动消费:由程序员来判断消费是否成功
        //设置为无ACK机制
        factory.setAcknowledgeMode(AcknowledgeMode.NONE);

        //设定消费者的线程数
        factory.setMaxConcurrentConsumers(5); //设置最大其实也不生效
        factory.setConcurrentConsumers(3);  //3个消费者

        //使用工厂生成监听器容器
        SimpleMessageListenerContainer container = factory.createListenerContainer();
        //绑定监听队列  监听器监听哪个队列
        container.setQueueNames("sys.msg.userId");
        //绑定监听器  用哪个监听器来监听
        container.setMessageListener(sysNoticeListener);
        return container;
    }
}

当队列中有消息时,会进行消费,程序运行后like this
在这里插入图片描述

场景模拟

我们虚拟一个场景,用RabbitMQ实现一下
故事是这样的,当用户上线时,获得系统未读消息数量,上线期间,可以实时获取消息,下线后不再实时获取

也就是说,当用户上线时,返回当前队列中消息总数,上线期间,实时监听消息

具体实现

  • 编写RabbitConfig
@Configuration
public class RabbitConfig {
    //注入自定义的监听器
    @Resource
    private SysNoticeListener sysNoticeListener;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Bean("sysListenerContainer")
    public SimpleMessageListenerContainer listenerContainer(ConnectionFactory connectionFactory) {
        //创建监听器工厂
        SimpleRabbitListenerContainerFactory factory=new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);

        //ack机制  自动消费 或 手动消费
        //自动消费:无论消费是否成功都进行确认
        //手动消费:由程序员来判断消费是否成功
        //设置为无ACK机制
        factory.setAcknowledgeMode(AcknowledgeMode.NONE);


        //设定消费者的线程数
        factory.setMaxConcurrentConsumers(5); //设置最大其实也不生效
        factory.setConcurrentConsumers(3);  //3个消费者

        //使用工厂生 成监听器容器
        SimpleMessageListenerContainer container = factory.createListenerContainer();
        //绑定监听队列  监听器监听哪个队列
        //container.setQueueNames("sys.msg.userId");
        //绑定监听器  用哪个监听器来监听
        container.setMessageListener(sysNoticeListener);
        return container;
    }

    /**
     * 当服务器启动时,为每个用户生成一个队列用于接收系统消息
     * 队列名称规则:[sys.msg.userId]  sys.msg.1 sys.msg.2
     */
    @Bean("rabbitAdmin")
    public RabbitAdmin rabbitAdmin() {
        RabbitAdmin admin = new RabbitAdmin(rabbitTemplate);
        List<Queue> queues = new ArrayList<>();
        //创建队列
        IntStream.range(0,2).forEach(i->{
            queues.add(new Queue("sys.msg."+i));
        });

        //创建交换机
        //因是系统通知,所以创建Fanout交换机
        FanoutExchange exchange = new FanoutExchange("sys.notice.fanout");

        //交换机和队列绑定
        List<Binding> bindings = new ArrayList<>();
        for (Queue q : queues) {
            bindings.add(BindingBuilder.bind(q).to(exchange));
        }

        admin.declareExchange(exchange);
        IntStream.range(0,2).forEach(i->{
            admin.declareQueue(queues.get(i));
            admin.declareBinding(bindings.get(i));
        });
        return admin;
    }

}

rabbitAdmin方法中可以根据实际情况,增大循环数,为每一个用户创建一个接收系统通知的队列

  • 编写Controller
@RestController
@RequestMapping("user")
public class ConsumeController {
    //注入容器
    @Resource(name = "sysListenerContainer")
    private SimpleMessageListenerContainer container;
    //注入监听器
    @Resource
    private SysNoticeListener listener;
    //注入RabbitAdmin
    @Resource(name = "rabbitAdmin")
    private RabbitAdmin rabbitAdmin;

    /**
     * 登录时首先获得未读消息数量  然后进行实时消费
     * @param userId
     * @return
     */
    @GetMapping("login/{userId}")
    public String login(@PathVariable String userId) {
        /*
        登录成功后 首先获取离线消息 即消息的数量
         */

        //根据UserId拼接自己的队列名称
        String queueName = "sys.msg." + userId;
        Properties queueProperties = rabbitAdmin.getQueueProperties(queueName);
        //通过属性值获取消息总量
        int count = Integer.parseInt(queueProperties.get("QUEUE_MESSAGE_COUNT").toString());
        System.out.println("您未上线的时间里,有" + count + "条消息在等您");
        //清空队列 一定要清空
        rabbitAdmin.purgeQueue(queueName);

        /*
        上线后,消息是实时消费的
         */

        //监听容器绑定监听队列
        container.setQueueNames(queueName);

        return "success";

    }


    /**
     * 退出登录时 移除监听队列
     *
     * @return
     */
    @GetMapping("/logout/{userId}")
    public String logout(@PathVariable("userId") String userId) {
        String queueName = "sys.msg." + userId;
        container.removeQueueNames(queueName);
        return "logout success";
    }
}

也就是在用户成功登录后才进行将监听容器和队列相绑定,在用户退出登录后,从监听容器中移除该队列

  • 编写监听器Listener
@Component
public class SysNoticeListener implements ChannelAwareMessageListener {

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

       /*
       模拟业务中对实时消息的处理
        */

        //根据队列名称获取用户id
        MessageProperties messageProperties = message.getMessageProperties();
        //获取队列名称
        String consumerQueue = messageProperties.getConsumerQueue();
        //获取消息体
        byte[] body = message.getBody();
        System.out.println(consumerQueue + ":收到实时消息【" + new String(body) + "]");
    }
}

监听器实际上处理的是实时消息的处理

  • 最终效果
    在这里插入图片描述
    登录哪一个用户,哪一个用户就可以接收实时消息

  • 补充一点关于ACK机制
    如果在RabbitConfig中设置了ACK为手动模式,那么就需要在监听器中增加try-catch进行手动处理

RabbitConfig中
在这里插入图片描述

监听器中
在这里插入图片描述
消息的处理需要放在try中,如果消息消费失败,则将消息返回给队列

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 在 Spring Boot 中,你可以通过 RabbitMQ 的 `x-delayed-message` 插件来实现延时队列,而不需要使用额外的插件或库。下面是实现步骤: 1. 添加依赖 在 `pom.xml` 文件中添加 RabbitMQ 的依赖: ```xml <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>2.3.12.RELEASE</version> </dependency> ``` 2. 配置 RabbitMQ 的 `x-delayed-message` 插件 在 RabbitMQ 中,你需要先安装 `x-delayed-message` 插件。你可以通过 `rabbitmq-plugins` 命令来安装插件: ``` rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` 或者,你可以在 `rabbitmq.conf` 文件中添加以下配置,然后重启 RabbitMQ: ``` plugins.rabbitmq_delayed_message_exchange = {git, "https://github.com/rabbitmq/rabbitmq-delayed-message-exchange", {branch, "master"}} ``` 3. 配置 RabbitMQ 的连接信息 在 `application.properties` 中添加 RabbitMQ 的连接信息: ```properties spring.rabbitmq.host=your-rabbitmq-host spring.rabbitmq.port=5672 spring.rabbitmq.username=your-rabbitmq-username spring.rabbitmq.password=your-rabbitmq-password ``` 4. 定义队列和交换器 在 Spring Boot 中,你可以使用 `@Configuration` 和 `@Bean` 注解来定义队列和交换器。下面是一个例子: ```java @Configuration public class RabbitConfig { @Bean public Queue delayedQueue() { return QueueBuilder.durable("delayed.queue") .withArgument("x-dead-letter-exchange", "normal.exchange") .withArgument("x-dead-letter-routing-key", "normal.routingkey") .build(); } @Bean public CustomExchange delayedExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed.exchange", "x-delayed-message", true, false, args); } @Bean public Binding binding() { return BindingBuilder.bind(delayedQueue()) .to(delayedExchange()) .with("delayed.routingkey") .noargs(); } } ``` 在上面的例子中,我们定义了一个 `delayedQueue` 队列,它的死信交换器是 `normal.exchange`,死信路由键是 `normal.routingkey`。我们还定义了一个 `delayedExchange` 交换器,它的类型是 `x-delayed-message`,并将 `x-delayed-type` 属性设置为 `direct`。最后,我们将 `delayedQueue` 队列绑定到 `delayedExchange` 交换器上,并使用路由键 `delayed.routingkey`。 5. 发送延时消息 你可以使用 `RabbitTemplate` 类来发送消息到 `delayedQueue` 队列。在发送消息时,你需要将消息的 `headers` 属性设置为 `x-delay`,并将值设置为消息的延时时间(单位为毫秒)。 ```java @Autowired private RabbitTemplate ### 回答2: 在Spring Boot中实现RabbitMQ延时队列需要以下几个步骤: 1. 首先,我们需要定义一个交换机(Exchange),用于将消息发送到延时队列中。可以使用DirectExchange、TopicExchange或FanoutExchange等不同类型的交换机。交换机的类型根据具体的业务需求而定。 2. 接下来,我们需要定义两个队列,一个为延时队列,另一个为业务队列。延时队列用于接收需要延时处理的消息,业务队列用于接收延时队列中处理完成的消息。 3. 创建并配置消息发送和接收的相关组件。使用RabbitTemplate来发送消息到延时队列创建一个消费者来接收延时队列中的消息并处理。 4. 在消息发送时,可以通过给消息设置不同的过期时间来实现延时功能。在发送消息时,将消息携带的延时时间设置为过期时间,然后发送到延时队列中。 5. 在消费者中,监听业务队列,当接收到延时队列中的消息时,进行相应的处理,例如发送邮件、生成报表等。 这样就实现了RabbitMQ延时队列的功能。通过设置消息的过期时间,可以控制消息何时被消费。延时队列可以在某个特定的时间点将消息转发到业务队列,完成后续处理。Spring Boot提供了简单而强大的集成,可以轻松实现延时队列的功能。 ### 回答3: 实现RabbitMQ延时队列的核心思想是利用RabbitMQ的插件(x-delayed-message)和Spring Boot消息中间件(RabbitTemplate)结合使用。 首先,确保在RabbitMQ服务中安装了插件。在RabbitMQ的安装目录下,执行以下命令: ``` rabbitmq-plugins enable rabbitmq_delayed_message_exchange ``` 接下来,在Spring Boot项目的pom.xml文件中添加RabbitMQ的依赖: ```xml <dependencies> <!-- RabbitMQ --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> </dependencies> ``` 然后,创建一个配置类,用于连接RabbitMQ服务和创建延时队列: ```java @Configuration public class RabbitMQConfig { @Autowired private Environment env; @Bean public ConnectionFactory connectionFactory() { CachingConnectionFactory connectionFactory = new CachingConnectionFactory(); connectionFactory.setAddresses(env.getProperty("spring.rabbitmq.addresses")); connectionFactory.setUsername(env.getProperty("spring.rabbitmq.username")); connectionFactory.setPassword(env.getProperty("spring.rabbitmq.password")); return connectionFactory; } @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory()); rabbitTemplate.setMessageConverter(jsonMessageConverter()); return rabbitTemplate; } @Bean public MessageConverter jsonMessageConverter() { return new Jackson2JsonMessageConverter(); } @Bean public Exchange delayedExchange() { Map<String, Object> args = new HashMap<>(); args.put("x-delayed-type", "direct"); return new CustomExchange("delayed-exchange", "x-delayed-message", true, false, args); } @Bean public Queue delayedQueue() { return new Queue("delayed-queue", true); } @Bean public Binding delayedBinding() { return BindingBuilder.bind(delayedQueue()).to(delayedExchange()).with("delayed-routing-key").noargs(); } } ``` 在上述代码中,我们创建了一个自定义的Exchange,将其类型设置为"x-delayed-message",并创建了一个延时队列,将其绑定在这个Exchange上。这样,消息发送到这个Exchange时,会根据消息中的延时时间属性进行延时处理。 最后,我们可以通过RabbitTemplate发送延时消息: ```java @Service public class RabbitMQService { @Autowired private RabbitTemplate rabbitTemplate; public void sendDelayedMessage(String message, int delayTime) { rabbitTemplate.convertAndSend("delayed-exchange", "delayed-routing-key", message, new MessagePostProcessor() { @Override public Message postProcessMessage(Message message) throws AmqpException { message.getMessageProperties().setHeader("x-delay", delayTime); return message; } }); } } ``` 在上述代码中,我们通过rabbitTemplate将消息发送到名为"delayed-exchange"的Exchange上,并设置消息的延时时间属性"x-delay"。最后,通过"convertAndSend"方法发送消息。 以上就是使用Spring Boot实现RabbitMQ延时队列的简单示例。通过这种方式,我们可以轻松地实现消息的延时处理,使得系统更加灵活和高效。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值