RabbitMQ:AMQP 原理、Spring AMQP 实战与 Work Queue 模型

一、AMQP 与 Spring AMQP

(一)AMQP 协议简介

AMQP(Advanced Message Queuing Protocol,高级消息队列协议) 是一种用于在应用程序之间传递业务消息的 开放标准协议。

它的核心特点包括:

**与编程语言无关:**Java、Python、Go、C# 等都可以基于 AMQP 进行通信

**与平台无关:**不依赖具体操作系统或运行环境

**解耦系统组件:**生产者与消费者无需直接通信

**天然适合微服务架构:**符合服务自治、松耦合的设计思想

在微服务或分布式系统中,AMQP 常用于:

服务之间的异步通信

削峰填谷(应对突发流量)

系统解耦与可靠消息传递

RabbitMQ 正是 AMQP 协议最经典、最成熟的实现之一。


(二)Spring AMQP 简介

Spring AMQP 是 Spring 官方基于 AMQP 协议提供的一套消息中间件解决方案,用于简化 AMQP 在 Spring 应用中的使用。

Spring AMQP 并不是一个消息队列,而是一个 对 AMQP 协议的 Java 抽象与封装,主要包含两个核心模块:

spring-amqp

定义了 AMQP 的核心抽象接口(Message、Exchange、Queue、Template 等)

与具体消息中间件实现解耦

spring-rabbit

spring-amqp 的默认实现

基于 RabbitMQ Java Client

负责与 RabbitMQ 服务端进行真正的网络通信

在实际开发中:

我们面向 Spring AMQP 的 API 编程

由 spring-rabbit 在底层完成与 RabbitMQ 的交互

这也是 Spring 生态一贯的设计思想:

面向接口编程,屏蔽底层实现细节,提升开发效率。


二、实践

(一)快速掌握 Spring AMQP 的基本使用方式

1. 实践目标

本实践通过一个最简单的 RabbitMQ 队列模型,快速掌握 Spring AMQP 的基本使用方式,具体目标如下:

通过 RabbitMQ 控制台 手动创建队列 simple.queue

在 publisher 服务 中,使用 RabbitTemplate 向队列发送消息

在 consumer 服务 中,使用 @RabbitListener 监听并消费队列消息

整体结构清晰、步骤完整,适合作为 Spring AMQP 的入门示例。


2. 创建队列 simple.queue

**(1)**创建普通用户

根据前一篇文章的步骤,新建一个 RabbitMQ 用户:

用户名:xiaoman 密码:1007 用户角色:management

说明:

management 权限 即可登录控制台并管理队列

不需要 administrator 权限


(2) 登录控制台并创建队列

浏览器访问:

java 复制代码
http://192.168.41.136:15672

使用 xiaoman 用户登录

进入 Queues 页面

新建队列:

Queue name:simple.queue

其他参数保持默认

队列创建完成后,可在列表中看到 simple.queue。

3. 创建项目并引入依赖

(1)项目结构

创建一个 Maven 父工程:

java 复制代码
rabbitmq_practice
├── publisher # 消息生产者
└── consumer # 消息消费者

(2) 父工程 pom.xml

在 rabbitmq_practice 的 pom.xml 中统一管理依赖:

java 复制代码
<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--  AMQP 依赖 ,包含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>
        </dependency>

子工程 publisher 和 consumer 继承该父工程即可。


4. 配置 RabbitMQ 连接信息

在 publisher 和 consumer 的 application.yml 中,配置 RabbitMQ 服务端信息:

java 复制代码
spring:
  rabbitmq:
    host: 192.168.41.136
    port: 5672
    virtual-host: /
    username: xiaoman
    password: 1007

说明:

两个服务连接的是 同一个 RabbitMQ

使用同一个虚拟主机 /


5. 发送消息(publisher)

Spring AMQP 提供了 RabbitTemplate,用于简化消息发送操作。

(1)编写测试类

在 publisher 模块中创建测试类:

java 复制代码
@SpringBootTest
public class SpringAmqpTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Test
    void testSendMessageQueue(){
        String queueName = "simple.queue";
        String msg = "hello, amqp";
        rabbitTemplate.convertAndSend(queueName, msg);
    }

}

(2) 运行测试

运行测试方法后:

消息会被发送到 simple.queue

在 RabbitMQ 控制台中可看到 Ready 数量增加

此时也可以手动点击 Get Message(s) 查看消息内容。


6. 接收消息(consumer)

(1)启动类

在 consumer 模块中创建启动类:

java 复制代码
@SpringBootApplication
public class ConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerApplication.class,args);
    }
}

(2)编写消费者监听器

使用 @RabbitListener 声明式监听队列:

java 复制代码
@Slf4j
@Component
public class SimpleQueueListener {

    @RabbitListener(queues = "simple.queue")
    public void listenerSimpleQueue(String msg){
        System.out.println("消费者收到消息的simple.queue的消息:" + msg);
    }
}

(3)启动消费者

启动 ConsumerApplication 后:

Spring AMQP 会自动监听 simple.queue

当队列中有消息时,方法会被自动调用

控制台会打印接收到的消息内容

说明:

消息成功从 publisher 发送

被 consumer 正常消费


三、WorkQueue模式

Work Queue 用于 模拟多个消费者共同消费同一个队列中的任务,典型应用场景包括:

异步任务处理(如订单、日志、短信)

后台任务削峰

提升系统整体吞吐量

其核心目标是:

一个队列,多个消费者,任务被"分摊"执行,而不是被重复消费。


(一)实践目标

通过实验观察并理解:

RabbitMQ 默认的 Work Queue 分发策略

不同消费能力的消费者在默认模式下的表现

如何通过 prefetch(公平调度) 优化任务分配

实验设计如下:

  1. 创建队列 work.queue

  2. publisher 在 1 秒内发送 50 条消息

  3. consumer 中定义 两个消费者同时监听同一队列

  4. 消费者 1:快(≈ 每秒 50 条)

  5. 消费者 2:慢(≈ 每秒 5 条)


(二)创建队列 work.queue

通过 RabbitMQ 管理控制台创建队列:

Queue Name:work.queue

其他参数保持默认


(三)publisher 发送 50 条消息(1 秒内)

在 publisher 测试类中编写如下方法:

java 复制代码
@Test
    void sendWorkQueueMessage() throws InterruptedException {
        String queueName = "work.queue";

        for (int i = 1; i <= 50; i++ ){
            String msg = "任务消息: " + i;
            rabbitTemplate.convertAndSend(queueName, msg);
            Thread.sleep(20);
        }
        System.out.println("50 条消息发送完成");
    }

(四)consumer:定义两个不同处理能力的消费者

这里通过 两个监听方法,模拟两个独立消费者。


消费者 1:处理快(≈ 每秒 50 条)

思路说明:

每条消息处理耗时 ≈ 20ms

1 秒可处理约 50 条

java 复制代码
@Slf4j
@Component
public class WorkQueueConsumer1 {

    @RabbitListener(queues = "work.queue")
    public void handleMessage(String msg) throws InterruptedException {
        // 模拟快消费者
        Thread.sleep(20);
        System.out.println("【消费者1-快】处理消息:" + msg);
    }
}

消费者 2:处理慢(≈ 每秒 5 条)

思路说明:

每条消息处理耗时 ≈ 200ms

1 秒只能处理 5 条

java 复制代码
@Slf4j
@Component
public class WorkQueueConsumer2 {

    @RabbitListener(queues = "work.queue")
    public void handleMessage(String msg) throws InterruptedException {
        // 模拟慢消费者
        Thread.sleep(200);
        System.out.println("【消费者2-慢】处理消息:" + msg);
    }
}

(五)默认模式运行现象(轮询分发)

运行步骤

  1. 启动 consumer 服务

  2. 执行 publisher 测试方法

现象观察

按直觉:

消费者 1 更快,应该消费更多消息

但实际结果是:

两个消费者 几乎各消费一半消息

慢消费者明显"拖慢"整体处理速度

原因分析

RabbitMQ 默认采用轮询(Round-Robin)分发策略:

不关心消费者处理速度

只按"消费者数量"平均分发消息

这正是 Work Queue 默认模式下最容易踩的一个"坑"。


(六)Work Queue 的核心:公平调度(Prefetch)

为了避免慢消费者拖垮系统,需要开启 公平调度机制

其思想是:

谁处理得快,谁就多拿任务;处理慢的消费者自动少拿。

1. 配置 prefetch

在 application.yml 中添加配置:

java 复制代码
    listener:
      simple:
        prefetch: 1
        acknowledge-mode: auto

2. 参数含义说明

prefetch: 1

每个消费者 同一时刻最多只持有 1 条未确认消息

acknowledge-mode: auto

消费成功 → 自动 ACK

消费异常 → 自动 NACK


**(七)**开启公平调度后的效果

重新启动 consumer,再次发送消息,可以明显观察到:

快消费者不断收到新消息

慢消费者处理完一条后,很久才拿到下一条

消息分配明显 倾向处理快的一方


(八)为什么这才是真正的"公平"?

对比项 默认模式 prefetch = 1
分发依据 消费者数量 消费者处理能力
慢消费者 仍然分到一半 自动降权
快消费者 被拖慢 多劳多得
系统吞吐

公平 ≠ 平均 公平 = 谁快谁多拿

Work Queue 的公平调度,本质上是通过 限制未确认消息数量,让 RabbitMQ 能够根据消费者的实时处理能力动态分配任务,从而显著提升整体吞吐量与系统效率。


四、总结

本文从 AMQP 协议的基本概念出发,介绍了 Spring AMQP 在 Spring 生态中的定位与作用,并通过完整的实践示例,演示了 Spring Boot 如何快速集成 RabbitMQ。

在实践部分,我们首先通过最简单的 Simple Queue 模式,掌握了消息发送与接收的基本流程,包括队列创建、RabbitTemplate 发送消息以及 @RabbitListener 消费消息。在此基础上,进一步引入 Work Queue 模式,通过多个消费者共同消费同一队列中的任务,直观地观察了 RabbitMQ 默认分发策略在不同消费能力下的表现。

最后,通过对 Work Queue 场景的分析,引出了 prefetch(公平调度) 的概念,为后续理解消息积压、消费均衡以及高并发场景下的消息处理优化打下基础。

通过这些实践,可以清晰地体会到消息队列在 系统解耦、异步处理与削峰填谷 等场景中的价值,也为在微服务架构中更合理地使用 RabbitMQ 提供了实践参考。

相关推荐
萧曵 丶2 小时前
Java Stream 实际用法详解
java·stream·lambda
dvlinker2 小时前
动态代理技术实战测评—高效解锁Zillow房价历史
android·java·数据库
喵手2 小时前
JVM 基础知识:深入理解 Java 的运行时环境!
java·jvm·jvm基础·java运行环境
简烦2 小时前
外层事务的 afterCommit 中调用内层事务方法时,内层事务的 TransactionSynchronization 注册失败 / 不执行
java·spring
峥嵘life2 小时前
Android16 EDLA 认证BTS测试Failed解决总结
android·java·linux·运维·学习
wniuniu_2 小时前
object->osd
android·java·数据库
猫头虎2 小时前
IntelliJ IDEA 2025.3 最新变化:值得更新吗?
java·开发语言·ide·人工智能·intellij-idea·idea·gitcode
猫豆~2 小时前
ceph分布式存储——1day
java·linux·数据库·sql·云计算
爱吃烤鸡翅的酸菜鱼2 小时前
Spring Boot 注解全栈指南:涵盖 Bean 注册、配置加载、请求映射、事务控制、数据校验等一网打尽
java·开发语言·spring boot·后端·spring