【RabbitMQ】SpringBoot 整合 RabbitMQ

文章目录

  • [1. 创建 SpringBoot 项目](#1. 创建 SpringBoot 项目)
  • [2. 工作队列模式](#2. 工作队列模式)
    • [2.1 编写生产者代码](#2.1 编写生产者代码)
    • [2.2 编写消费者代码](#2.2 编写消费者代码)
    • [2.3 编写多个消费者代码](#2.3 编写多个消费者代码)
  • [3. Publish/Subscribe(发布订阅模式)](#3. Publish/Subscribe(发布订阅模式))
    • [3.1 编写生产者代码](#3.1 编写生产者代码)
    • [3.2 编写消费者代码](#3.2 编写消费者代码)
    • [3.3 运行程序](#3.3 运行程序)
  • [4. Routing(路由模式)](#4. Routing(路由模式))
    • [4.1 编写生产者代码](#4.1 编写生产者代码)
    • [4.2 编写消费者代码](#4.2 编写消费者代码)
    • [4.3 运行程序](#4.3 运行程序)
  • [5. 通配符模式](#5. 通配符模式)
    • [5.1 编写生产者代码](#5.1 编写生产者代码)
    • [5.2 编写消费者代码](#5.2 编写消费者代码)
    • [5.3 运行程序](#5.3 运行程序)

对于 RabbitMQ 开发,Spring 也提供了一些便利。SpringRabbitMQ 的官方文档对此均有介绍。

下面来看如何基于 SpringBoot 进行 RabbitMQ 的开发。咱们只演示部分常用的工作模式:

  • 工作队列模式(Work Queues)
  • 发布订阅模式(Publish / Subscribe)
  • 路由模式(Routing)
  • 通配符模式(Topics)

1. 创建 SpringBoot 项目

新建一个空的项目如下所示:

然后添加依赖:

创建好以后,把下面几个没有用的给删除掉

并且依赖已经是添加好了的

然后添加 rabbitmq 的服务配置,这里使用 yml 的格式

写法一

yml 复制代码
#配置RabbitMQ的基本信息
spring:
  rabbitmq:
    host: 
    port: 5672  #默认为5672
    username: 
    password: 
    virtual-host:   #默认值为/

或者还有下面这种写法(推荐)

yml 复制代码
#配置RabbitMQ的基本信息
  rabbitmq:
    #amqp://username:password@Ip:port/virtual-host
    addresses: amqp://edison:edison@IP:5672/my_app_vhost

注意:RabbitMQ 的通信端口是 5672,管理后台端口是 15672。

下面就可以开始完成代码的编写了。

2. 工作队列模式

步骤:

  • 1、引入依赖
  • 2、编写 yml 配置,基本信息配置
  • 3、编写生产者代码
  • 4、编写消费者代码
    • a、定义监听类,使用 @RabbitListener 注解完成队列监听
  • 5、运行观察结果

2.1 编写生产者代码

先把队列名称定义为一个常量

java 复制代码
public static final String WORK_QUEUE = "workQueue";

声明队列

java 复制代码
package com.edison.rabbitmq.config;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfig {
    // 1. 工作模式队列
    @Bean("workQueue")
    public Queue workQueue() {
        return QueueBuilder.durable(Constants.WORK_QUEUE).build();
    }
}

为了方便测试,我们通过接口来发送消息

java 复制代码
import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/producer")
@RestController
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate; // 可以理解为rabbitmq客户端
    
    @RequestMapping("/work")
    public String work() {
        // 使用内置交换机, RoutingKey和队列名称一致
        rabbitTemplate.convertAndSend("", Constants.WORK_QUEUE, "hello spring amqp: work...");
        return "发送成功";
    }
}

运行代码

然后在浏览器测试接口

同时从日志中也可以看到创建了一个新的连接

并且管理后台已经有队列信息了

由此可知,当我们把 SpringBoot 程序启动以后,它其实并没有帮我们创建队列,而是在我们发送消息的时候,才会进行创建。

2.2 编写消费者代码

定义监听类

java 复制代码
package com.edison.rabbitmq.listener;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class WorkListener {
    
    @RabbitListener(queues = Constants.WORK_QUEUE)
    public void queueListener(Message message) {
        System.out.println("["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
    }
}

@RabbitListener 是 Spring 框架中用于监听 RabbitMQ 队列的注解,通过使用这个注解,可以定义一个方法,以便从 RabbitMQ 队列中接收消息。该注解支持多种参数类型,这些参数类型代表了从 RabbitMQ 接收到的消息和相关信息。

以下是一些常用的参数类型:

  • 1、String:返回消息的内容
  • 2、Message(org.springframework.amqp.core.Message):Spring AMQP 的 Message 类,返回原始的消息体以及消息的属性,如消息 ID,内容,队列信息等。
  • 3、Channel(com.rabbitmq.client.Channel):RabbitMQ 的通道对象,可以用于进行更高级的操作,如手动确认消息。

消费者测试,打印消息内容

消息内容如下所示:

复制代码
[workQueue] 接收到消息: (Body:'hello spring amqp: work...' MessageProperties [headers={}, contentType=text/plain, 
contentEncoding=UTF-8, 
contentLength=0, 
receivedDeliveryMode=PERSISTENT, 
priority=0, 
redelivered=true, 
receivedExchange=, 
receivedRoutingKey=workQueue, 
deliveryTag=1, 
consumerTag=amq.ctag-FXo4xTJQt74J4YDbwOS0GQ, consumerQueue=workQueue])

并且此时在管理后台可以看到,消息已经被消费了。

2.3 编写多个消费者代码

代码如下所示:

java 复制代码
package com.edison.rabbitmq.listener;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class WorkListener {

    @RabbitListener(queues = Constants.WORK_QUEUE)
    public void queueListener1(Message message) {
        System.out.println("listener 1 ["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
    }

    @RabbitListener(queues = Constants.WORK_QUEUE)
    public void queueListener2(Message message) {
        System.out.println("listener 2 ["+Constants.WORK_QUEUE+"] 接收到消息: "+message);
    }
}

然后修改生产者代码,让其一次性发送多条消息

java 复制代码
package com.edison.rabbitmq.controller;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/producer")
@RestController
public class ProducerController {
    @Autowired
    private RabbitTemplate rabbitTemplate; // 可以理解为rabbitmq客户端

    @RequestMapping("/work")
    public String work() {
        for (int i = 0; i < 10; i ++)
        {
            // 使用内置交换机, RoutingKey和队列名称一致
            rabbitTemplate.convertAndSend("", Constants.WORK_QUEUE, "hello spring amqp: work...");
        }
        return "发送成功";
    }
}

然后运行结果,如下所示:

3. Publish/Subscribe(发布订阅模式)

在发布/订阅模型中,多了一个Exchange角色。

Exchange常见有三种类型,分别代表不同的路由规则:

  • a) Fanout:广播,将消息交给所有绑定到交换机的队列(Publish/Subscribe模式)
  • b) Direct:定向,把消息交给符合指定routing key的队列(Routing模式)
  • c) Topic:通配符,把消息交给符合routing pattern(路由模式)的队列(Topics模式)

我们先来看 Fanout 广播路由规则。

3.1 编写生产者代码

和简单模式的区别是:需要创建交换机,并且绑定队列和交换机。

定义队列和交换机

java 复制代码
// 发布订阅模式
public static final String FANOUT_QUEUE1 = "fanout.queue1";
public static final String FANOUT_QUEUE2 = "fanout.queue2";
public static final String FANOUT_EXCHANGE = "fanout.exchange";

声明队列和交换机

java 复制代码
// 2. 发布订阅模式
// 声明2个队列, 观察是否两个队列都收到了消息
@Bean("fanoutQueue1")
public Queue fanoutQueue1() {
    return QueueBuilder.durable(Constants.FANOUT_QUEUE1).build();
}
@Bean("fanoutQueue2")
public Queue fanoutQueue2() {
    return QueueBuilder.durable(Constants.FANOUT_QUEUE2).build();
}
// 声明交换机
@Bean("fanoutExchange")
public FanoutExchange fanoutExchange() {
    return ExchangeBuilder.fanoutExchange(Constants.FANOUT_EXCHANGE).durable(true).build();
}

绑定队列和交换机

java 复制代码
// 声明交换机和队列的绑定
@Bean("fanoutQueueBinding1")
public Binding fanoutQueueBinding1(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange, @Qualifier("fanoutQueue1") Queue queue) {
    return BindingBuilder.bind(queue).to(fanoutExchange);
}
@Bean("fanoutQueueBinding2")
public Binding fanoutQueueBinding2(@Qualifier("fanoutExchange") FanoutExchange fanoutExchange, @Qualifier("fanoutQueue1") Queue queue) {
    return BindingBuilder.bind(queue).to(fanoutExchange);
}

然后使用接口发送消息

java 复制代码
@RequestMapping("/fanout")
public String fanout() {
    // routingKey为空, 表示所有队列都可以收到消息
    rabbitTemplate.convertAndSend(Constants.FANOUT_EXCHANGE, "", "hello spring amqp: fanout...");
    return "发送成功";
}

然后启动程序,观察结果,可以看到队列已经和交换机绑定好了

然后再通过接口发送消息,可以看到此时队列中已经有了消息

3.2 编写消费者代码

交换机和队列的绑定关系及声明已经在生产方写完,所以消费者不需要再写了。

定义监听类,处理接收到的消息即可。

java 复制代码
package com.edison.rabbitmq.listener;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class FanoutListener {

    @RabbitListener(queues = Constants.FANOUT_QUEUE1)
    public void queueListener1(String message) {
        System.out.println("队列["+Constants.FANOUT_QUEUE1+"] 接收到消息: "+message);
    }

    @RabbitListener(queues = Constants.FANOUT_QUEUE2)
    public void queueListener2(String message) {
        System.out.println("队列["+Constants.FANOUT_QUEUE2+"] 接收到消息: "+message);
    }
}

3.3 运行程序

先运行项目,调用接口 http://127.0.0.1:8080/producer/fanout 发送消息

然后监听类收到消息,并打印

4. Routing(路由模式)

交换机类型为 Direct 时,会把消息交给符合指定 routing key 的队列。

队列和交换机的绑定,不是任意的绑定了,而是要指定一个 RoutingKey(路由 key)

消息的发送方在向 Exchange 发送消息时,也需要指定消息的 RoutingKey。

Exchange 也不再把消息交给每一个绑定的 key,而是根据消息的 RoutingKey 进行判断,只有队列的 RoutingKey 和消息的 RoutingKey 完全一致,才会接收到消息。

4.1 编写生产者代码

和发布订阅模式的区别就是:交换机类型不同,绑定队列的 RoutingKey 不同。

定义队列和交换机

java 复制代码
// 路由模式
public static final String DIRECT_QUEUE1 = "direct.queue1";
public static final String DIRECT_QUEUE2 = "direct.queue2";
public static final String DIRECT_EXCHANGE = "direct.exchange";

声明队列和交换机

java 复制代码
// 声明队列
@Bean("directQueue1")
public Queue directQueue1() {
    return QueueBuilder.durable(Constants.DIRECT_QUEUE1).build();
}
@Bean("directQueue2")
public Queue directQueue2() {
    return QueueBuilder.durable(Constants.DIRECT_QUEUE2).build();
}
// 声明交换机
@Bean("directExchange")
public DirectExchange directExchange() {
    return ExchangeBuilder.directExchange(Constants.DIRECT_EXCHANGE).durable(true).build();
}

绑定队列和交换机

java 复制代码
// 队列和交换机绑定
// 队列1绑定orange
@Bean("directQueueBinding1")
public Binding directQueueBinding1(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue1") Queue queue) {
    return BindingBuilder.bind(queue).to(directExchange).with("orange");
}
// 队列2绑定black,orange
@Bean("directQueueBinding2")
public Binding directQueueBinding2(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue2") Queue queue) {
    return BindingBuilder.bind(queue).to(directExchange).with("black");
}
@Bean("directQueueBinding3")
public Binding directQueueBinding3(@Qualifier("directExchange") DirectExchange directExchange, @Qualifier("directQueue2") Queue queue) {
    return BindingBuilder.bind(queue).to(directExchange).with("orange");
}

绑定关系如下图所示:

使用接口发送消息

java 复制代码
@RequestMapping("/direct/{routingKey}")
public String direct(@PathVariable("routingKey") String routingKey) { // 从路径中拿到routingKey, 需要使用PathVariable注解
    // routingkey作为参数传递
    rabbitTemplate.convertAndSend(Constants.DIRECT_EXCHANGE, routingKey, "hello spring amqp: direct, my routing Key is " + routingKey);
    return "发送成功";
}

然后运行程序,从管理平台上可以看到,此时队列没有任何消息

然后绑定关系如下

此时,我们通过接口 http://127.0.0.1:8080/producer/direct/orange 来发送 orange 消息

然后再通过接口 http://127.0.0.1:8080/producer/direct/black 发送 balck 消息,该消息只能由队列 2 拿到

4.2 编写消费者代码

交换机和队列的绑定关系及声明已经在生产方写完,所以消费者不需要再写了。

定义监听类,处理接收到的消息即可。

java 复制代码
package com.edison.rabbitmq.listener;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class DirectListener {

    @RabbitListener(queues = Constants.DIRECT_QUEUE1)
    public void queueListener1(String message) {
        System.out.println("队列["+Constants.DIRECT_QUEUE1+"] 接收到消息: "+message);
    }

    @RabbitListener(queues = Constants.DIRECT_QUEUE2)
    public void queueListener2(String message) {
        System.out.println("队列["+Constants.DIRECT_QUEUE2+"] 接收到消息: "+message);
    }
}

4.3 运行程序

运行项目

先调用接口 http://127.0.0.1:8080/producer/direct/orange 发送 routingkey 为 orange 的消息

观察后端日志,队列 1 和队列 2 收到消息

然后调用接口 http://127.0.0.1:8080/producer/direct/black 发送 routingkey 为 black 的消息

观察后端日志,队列 2 收到消息

5. 通配符模式

主题(Topics)和路由(Routing)模式的区别是:

  • 主题模式使用的交换机类型为主题(topic)(路由模式使用的交换机类型为直连(direct))
  • 主题类型的交换机在匹配规则上进行了扩展,绑定键(Binding Key)支持通配符匹配

5.1 编写生产者代码

和发布订阅模式的区别就是:交换机类型不同,绑定队列的路由键(RoutingKey)不同。

定义队列和交换机

java 复制代码
// 通配符模式
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String TOPIC_EXCHANGE = "topic.exchange";

声明队列和交换机

java 复制代码
// 声明队列
@Bean("topicQueue1")
public Queue topicQueue1() {
    return QueueBuilder.durable(Constants.TOPIC_QUEUE1).build();
}
@Bean("topicQueue2")
public Queue topicQueue2() {
    return QueueBuilder.durable(Constants.TOPIC_QUEUE2).build();
}
// 声明交换机
@Bean("topicExchange")
public TopicExchange topicExchange() {
    return ExchangeBuilder.topicExchange(Constants.TOPIC_EXCHANGE).durable(true).build();
}

绑定队列和交换机

java 复制代码
// 队列和交换机绑定
// 队列1绑定[*.orange.*]
@Bean("topicQueueBinding1")
public Binding topicQueueBinding1(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue1") Queue queue) {
    return BindingBuilder.bind(queue).to(topicExchange).with("*.orange.*");
}
// 队列2绑定[*.*.rabbit]
@Bean("topicQueueBinding2")
public Binding topicQueueBinding2(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue2") Queue queue) {
    return BindingBuilder.bind(queue).to(topicExchange).with("*.*.rabbit");
}
// 队列2绑定[lazy.#]
@Bean("topicQueueBinding3")
public Binding topicQueueBinding3(@Qualifier("topicExchange") TopicExchange topicExchange, @Qualifier("topicQueue2") Queue queue) {
    return BindingBuilder.bind(queue).to(topicExchange).with("lazy.#");
}

绑定关系如下图所示:

然后使用接口发送消息

java 复制代码
@RequestMapping("/topic/{routingKey}")
public String topic(@PathVariable("routingKey") String routingKey) { // 从路径中拿到routingKey, 需要使用PathVariable注解
    // routingkey作为参数传递
    rabbitTemplate.convertAndSend(Constants.TOPIC_EXCHANGE, routingKey, "hello spring amqp: topic, my routing Key is " + routingKey);
    return "发送成功";
}

然后运行程序,从管理平台上可以看到,此时队列没有任何消息

然后绑定关系如下

5.2 编写消费者代码

定义监听类,处理接收到的消息。

java 复制代码
package com.edison.rabbitmq.listener;

import com.edison.rabbitmq.constant.Constants;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class TopicListener {

    @RabbitListener(queues = Constants.TOPIC_QUEUE1)
    public void queueListener1(String message) {
        System.out.println("队列["+Constants.TOPIC_QUEUE1+"] 接收到消息: "+message);
    }

    @RabbitListener(queues = Constants.TOPIC_QUEUE2)
    public void queueListener2(String message) {
        System.out.println("队列["+Constants.TOPIC_QUEUE2+"] 接收到消息: "+message);
    }
}

5.3 运行程序

运行项目

调用接口 http://127.0.0.1:8080/producer/topic/green.orange.c 发送 routingkey 为 green.orange.c 的消息

观察后端日志,队列 1 收到消息

调用接口 http://127.0.0.1:8080/producer/topic/green.orange.rabbit 发送 routingkey 为 green.orange.rabbit 的消息

观察后端日志,队列 1 和队列 2 均收到消息

调用接口 http://127.0.0.1:8080/producer/topic/lazy.orange.rabbit 发送 routingkey 为 lazy.orange.rabbit 的消息

观察后端日志,队列 1 和队列 2 均收到消息

相关推荐
夕除2 小时前
spring boot 4
java·spring boot·后端
starsky762382 小时前
spring boot——前后端分离
java·spring boot·后端
i220818 Faiz Ul3 小时前
个人健康系统|健康管理|基于java+Android+微信小程序的个人健康系统设计与实现(源码+数据库+文档)
android·java·vue.js·spring boot·微信小程序·毕设·个人健康系统
河阿里18 小时前
SpringBoot:项目启动速度深度优化
java·spring boot·后端
阿丰资源18 小时前
基于SpringBoot的企业客户管理系统(附源码)
java·spring boot·后端
两年半的个人练习生^_^19 小时前
SpringBoot 项目使用 Jasypt 实现配置文件敏感信息加密
java·spring boot·后端
YOU OU21 小时前
SpringBoot
java·spring boot·spring
凯瑟琳.奥古斯特21 小时前
SpringBoot快速入门指南
java·开发语言·spring boot·后端·spring
代码漫谈21 小时前
基于 Spring Boot 3.2.x 的 Actuator 监控指南:从健康检查到企业级监控体系
java·spring boot·actuator 监控