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 提供了实践参考。

相关推荐
刀法如飞12 小时前
一款开箱即用的Spring Boot 4 DDD工程脚手架
java·后端·架构
一嘴一个橘子12 小时前
spring-aop 的 基础使用 -3 - 切点表达式 的提取、复用
java
Re_zero12 小时前
Java新手避坑:为什么我劝你放弃 scanner.nextInt()?
java
Good_Starry12 小时前
Java——反射
java
又是忙碌的一天13 小时前
SpringBoot 创建及登录、拦截器
java·spring boot·后端
fox_mt13 小时前
AI Coding - ClaudeCode使用指南
java·ai编程
毕设源码-郭学长13 小时前
【开题答辩全过程】以 基于SSM的高校运动会管理系统的设计与实现为例,包含答辩的问题和答案
java·eclipse
qq_54702617913 小时前
Maven 使用指南
java·maven
xiaolyuh12313 小时前
Arthas修改类(如加日志)的实现原理
java
栗子叶13 小时前
Java对象创建的过程
java·开发语言·jvm