SpringBoot 整合 RabbitMQ 入门

一、为什么要学 RabbitMQ?

RabbitMQ 是分布式项目常用消息中间件,核心解决"同步调用"痛点,通俗说就是解耦、削峰、异步,先懂作用再学整合,更易理解。

1.1 核心作用

  • 业务解耦:下单后无需同步调用支付、库存等服务,发送消息异步处理,避免一个服务故障导致全流程崩溃;

  • 流量削峰:秒杀等高并发场景,请求先进入队列,服务器按能力逐步消费,防止过载;

  • 异步处理:发送短信、日志等无需用户等待的操作,异步执行提升体验;

  • 分布式事务最终一致性:跨服务操作通过消息确认,确保数据一致(入门暂不深入)。

1.2 典型应用场景

  • 电商场景:下单→支付→库存→物流→短信,全流程异步解耦;

  • 日志场景:异步收集日志,避免阻塞业务;

  • 通知场景:注册、订单变更等短信/邮件异步发送;

  • 高并发场景:秒杀请求缓冲,防止服务器宕机。

1.3 核心组件

消息流转:生产者 → 交换机 → 队列 → 消费者,各组件作用如下:

  • 生产者:发送消息的一方(如下单服务);

  • 消费者:接收并处理消息的一方(如短信服务);

  • 队列:存储消息,先进先出,消费后删除;

  • 交换机:消息分发中心,按规则将消息转发到队列(核心);

  • 路由键:生产者携带的消息标识,用于交换机分发;

  • 绑定:关联交换机和队列,设置路由规则;

  • 虚拟主机:隔离消息资源,入门用默认"/"即可。

二、环境准备

推荐 Docker 安装(一键启动),无 Docker 环境可选择本地安装,步骤简洁可落地。

2.1 Docker 一键安装

前提:已安装 Docker,执行以下命令启动 RabbitMQ(含管理后台):

go 复制代码
# 拉取镜像(带管理后台)
docker pull rabbitmq:3-management
# 启动容器,设置账号密码
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=123456 --restart=always rabbitmq:3-management

验证:访问 http://localhost:15672,用 admin/123456 登录,能进入即成功。

2.2 本地安装(无 Docker)

RabbitMQ 依赖 Erlang,步骤如下(Windows 为例):

    1. 安装 Erlang
  • • 下载:https://www.erlang.org/downloads(推荐 25.x 版本,与 RabbitMQ 兼容);

  • • 安装后配置环境变量 ERlang_HOME(安装路径),Path 添加 %ERLANG_HOME%\bin;

  • • cmd 输入 erl -version,显示版本即成功。

    1. 安装 RabbitMQ
  • 常见问题 :guest 登录失败?在 rabbitmq.conf 添加 loopback_users = none,重启服务即可。

    2.3 Maven 依赖

    SpringBoot 一键整合,pom.xml 添加以下依赖:

    go 复制代码
    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.7.18</version>
            <relativePath/>
        </parent>
        <groupId>com.example</groupId>
        <artifactId>springboot-rabbitmq-intro</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <properties>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <!-- Web 依赖,用于测试接口 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- RabbitMQ 核心依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-amqp</artifactId>
            </dependency>
            <!-- Lombok 简化代码(可选) -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
        </dependencies>
    </project>

    三、SpringBoot 基础配置

    application.yml 配置 RabbitMQ 连接信息,注释详细,直接复制:

    go 复制代码
    spring:
      rabbitmq:
        host: localhost # 服务地址,服务器填IP
        port: 5672 # 消息通信端口
        username: admin # Docker账号,本地默认guest
        password: 123456 # Docker密码,本地默认guest
        virtual-host: / # 默认虚拟主机
        listener:
          simple:
            acknowledge-mode: auto # 自动确认消息,简化操作
            concurrency: 1 # 消费者并发数,入门设1
        publisher-confirm-type: none # 关闭生产者确认,入门简化
        publisher-returns: false # 关闭消息返回,入门简化

    四、模式一:Direct 直连交换机

    核心:一对一精准匹配,路由键与绑定键完全一致,消息才会分发到对应队列,适合点对点通信(如下单消息)。

    4.1 配置类(队列+交换机+绑定)

    go 复制代码
    import org.springframework.amqp.core.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class DirectConfig {
        // 声明队列(持久化,重启不丢失)
        @Bean
        public Queue directQueue() {
            return QueueBuilder.durable("direct.queue").autoDelete(false).exclusive(false).build();
        }
        // 声明直连交换机
        @Bean
        public DirectExchange directExchange() {
            return DirectExchange.directExchange("direct.exchange").durable(true).autoDelete(false).build();
        }
        // 绑定,路由键 direct.key
        @Bean
        public Binding directBinding(Queue directQueue, DirectExchange directExchange) {
            return BindingBuilder.bind(directQueue).to(directExchange).with("direct.key");
        }
    }

    4.2 生产者(发送消息)

    go 复制代码
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class DirectProducer {
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
        // 测试接口:http://localhost:8080/send/direct/消息内容
        @GetMapping("/send/direct/{msg}")
        public String sendDirect(@PathVariable String msg) {
            String message = "【Direct消息】" + msg;
            rabbitTemplate.convertAndSend("direct.exchange", "direct.key", message);
            return "发送成功:" + message;
        }
    }

    4.3 消费者(监听消息)

    go 复制代码
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    @Component // 必须交给Spring管理
    public class DirectConsumer {
        @RabbitListener(queues = "direct.queue") // 监听指定队列
        public void consumeDirect(String msg) {
            System.out.println("Direct消费者接收:" + msg);
        }
    }

    4.4 测试步骤

      1. 启动 RabbitMQ 和 SpringBoot 项目;
      1. 访问接口 http://localhost:8080/send/direct/HelloRabbitMQ
      1. 查看 IDEA 控制台,消费者输出消息即成功。

    4.5 避坑要点

    • • 路由键与绑定键必须完全一致,否则消息丢失;

    • • 队列/交换机需设为持久化,重启 RabbitMQ 不丢失;

    • • 消费者需加 @Component 注解,否则无法监听。

    五、模式二:Fanout 扇出交换机(广播模式)

    核心:一对多广播,无需路由键,消息会广播到所有绑定的队列,适合系统公告、活动通知等场景。

    5.1 配置类(多队列+交换机+绑定)

    go 复制代码
    import org.springframework.amqp.core.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class FanoutConfig {
        // 声明两个队列,接收广播消息
        @Bean
        public Queue fanoutQueue1() {
            return QueueBuilder.durable("fanout.queue1").build();
        }
        @Bean
        public Queue fanoutQueue2() {
            return QueueBuilder.durable("fanout.queue2").build();
        }
        // 声明扇出交换机
        @Bean
        public FanoutExchange fanoutExchange() {
            return FanoutExchange.fanoutExchange("fanout.exchange").durable(true).build();
        }
        // 两个队列都绑定到交换机(无需路由键)
        @Bean
        public Binding fanoutBinding1(Queue fanoutQueue1, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
        }
        @Bean
        public Binding fanoutBinding2(Queue fanoutQueue2, FanoutExchange fanoutExchange) {
            return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
        }
    }

    5.2 生产者(发送广播消息)

    go 复制代码
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class FanoutProducer {
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
        // 测试接口:http://localhost:8080/send/fanout/消息内容
        @GetMapping("/send/fanout/{msg}")
        public String sendFanout(@PathVariable String msg) {
            String message = "【Fanout广播消息】" + msg;
            rabbitTemplate.convertAndSend("fanout.exchange", "", message); // 路由键填空
            return "广播发送成功:" + message;
        }
    }

    5.3 消费者(多消费者监听)

    go 复制代码
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class FanoutConsumer {
        @RabbitListener(queues = "fanout.queue1")
        public void consumeFanout1(String msg) {
            System.out.println("Fanout消费者1接收:" + msg);
        }
    
        @RabbitListener(queues = "fanout.queue2")
        public void consumeFanout2(String msg) {
            System.out.println("Fanout消费者2接收:" + msg);
        }
    }

    5.4 测试与避坑

    测试:访问接口后,两个消费者都会接收消息;

    避坑:扇出交换机无需路由键,填写也会被忽略,绑定即接收。

    六、模式三:Topic 主题交换机

    核心:模糊匹配路由键,通过通配符实现多对多路由,适合复杂场景(如按模块分发消息)。

    6.1 通配符规则

    • # :匹配1个或多个单词(如 order.# 匹配 order.createorder.pay.success);

    • * :匹配1个单词(如 order.* 匹配 order.create,不匹配 order.pay.success)。

    6.2 配置类(模糊绑定)

    go 复制代码
    import org.springframework.amqp.core.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class TopicConfig {
        // 3个队列,不同绑定键
        @Bean
        public Queue topicQueue1() { return QueueBuilder.durable("topic.queue1").build(); } // order.#
        @Bean
        public Queue topicQueue2() { return QueueBuilder.durable("topic.queue2").build(); } // order.pay.*
        @Bean
        public Queue topicQueue3() { return QueueBuilder.durable("topic.queue3").build(); } // *.login
    
        // 主题交换机
        @Bean
        public TopicExchange topicExchange() {
            return TopicExchange.topicExchange("topic.exchange").durable(true).build();
        }
    
        // 绑定不同规则
        @Bean
        public Binding topicBinding1(Queue topicQueue1, TopicExchange topicExchange) {
            return BindingBuilder.bind(topicQueue1).to(topicExchange).with("order.#");
        }
        @Bean
        public Binding topicBinding2(Queue topicQueue2, TopicExchange topicExchange) {
            return BindingBuilder.bind(topicQueue2).to(topicExchange).with("order.pay.*");
        }
        @Bean
        public Binding topicBinding3(Queue topicQueue3, TopicExchange topicExchange) {
            return BindingBuilder.bind(topicQueue3).to(topicExchange).with("*.login");
        }
    }

    6.3 生产者(不同路由键消息)

    go 复制代码
    import org.springframework.amqp.rabbit.core.RabbitTemplate;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class TopicProducer {
        @Autowired
        private RabbitTemplate rabbitTemplate;
    
        // 测试1:order.create(匹配queue1)
        @GetMapping("/send/topic/order/create")
        public String sendTopic1() {
            String msg = "订单创建:10001";
            rabbitTemplate.convertAndSend("topic.exchange", "order.create", msg);
            return "发送成功:" + msg;
        }
    
        // 测试2:order.pay.success(匹配queue1、queue2)
        @GetMapping("/send/topic/order/pay/success")
        public String sendTopic2() {
            String msg = "订单支付:10001,99元";
            rabbitTemplate.convertAndSend("topic.exchange", "order.pay.success", msg);
            return "发送成功:" + msg;
        }
    
        // 测试3:user.login(匹配queue3)
        @GetMapping("/send/topic/user/login")
        public String sendTopic3() {
            String msg = "用户登录:admin";
            rabbitTemplate.convertAndSend("topic.exchange", "user.login", msg);
            return "发送成功:" + msg;
        }
    }

    6.4 消费者与测试

    go 复制代码
    import org.springframework.amqp.rabbit.annotation.RabbitListener;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TopicConsumer {
        @RabbitListener(queues = "topic.queue1")
        public void consumeTopic1(String msg) {
            System.out.println("Topic消费者1(order.#):" + msg);
        }
    
        @RabbitListener(queues = "topic.queue2")
        public void consumeTopic2(String msg) {
            System.out.println("Topic消费者2(order.pay.*):" + msg);
        }
    
        @RabbitListener(queues = "topic.queue3")
        public void consumeTopic3(String msg) {
            System.out.println("Topic消费者3(*.login):" + msg);
        }
    }

    测试:依次访问3个接口,控制台输出与预期匹配即成功;

    避坑:路由键需用点分隔,否则通配符无效,无匹配队列则消息丢失。

    七、3种交换机对比与选型

    交换机类型 核心特点 路由规则 适用场景
    Direct(直连) 一对一,精准路由 路由键与绑定键完全一致 点对点通信(下单、个人通知)
    Fanout(扇出) 一对多,广播 无需路由键,广播到所有绑定队列 系统公告、活动通知
    Topic(主题) 多对多,模糊匹配 通过 #、* 通配符匹配 复杂路由(按模块分发消息)

    八、常见问题与解决方案(避坑指南)

      1. 消息发送成功,消费者收不到?
    • • 原因:绑定关系错误、消费者无 @Component、队列未持久化;

    • • 解决方案:检查绑定、添加注解、重启服务。

    1. 管理后台登录失败?
    • • 原因:账号密码错误、guest 未开启远程登录;

    • • 解决方案:核对账号,添加 loopback_users = none 并重启。

    1. 项目启动报错:无法连接 RabbitMQ?
    • • 原因:RabbitMQ 未启动、配置错误、端口未开放;

    • • 解决方案:启动 RabbitMQ、核对配置、开放 5672/15672 端口。

    学习本就是一个长期积累的过程,没有捷径,唯有坚持。希望这些干货能够真正帮到你,学以致用,不断提升,在自己的领域里越走越远。喜欢本文,别忘了点赞、在看、转发,我们下期干货继续!

相关推荐
wan_jm2 小时前
Go Web 开发提速 5(gos):数据库代码全自动生成 —— 多库统一+零硬编码+极致复用
后端
一叶之政2 小时前
C++ 系统学习日记・第 06 天|流程结构:循环语句(while+do-while+for 全解 + 嵌套)
后端
小龙报2 小时前
【数据结构与算法】一文拿捏链式二叉树:遍历 + 统计 + 层序 + 完全树
java·c语言·开发语言·c++·人工智能·语言模型·visual studio
TE-茶叶蛋2 小时前
Spring 高级机制:循环依赖 + AOP + @Transactional 失效原理
java·后端·spring
juniperhan2 小时前
Flink 系列第18篇:Flink 动态表、连续查询与 Changelog 机制
java·大数据·数据仓库·分布式·flink
aXin_ya2 小时前
微服务(高级) 8
java·数据库·微服务
绿草在线2 小时前
03.JakartaEE11+Thymeleaf实现图书列表功能
java
逻辑驱动的ken2 小时前
Java高频面试考点场景题15
java·开发语言·深度学习·面试·职场和发展·高效学习
juniperhan2 小时前
Flink 系列第19篇:深入理解 Flink SQL 的时间语义与时区处理:从原理到实战
java·大数据·数据仓库·分布式·sql·flink