作者:没有四次元口袋的蓝胖
日期:2026-10-16
标签:RabbitMQ, 安装, HelloWorld, 工作模型
RabbitMQ知识梳理(1)
写在前面
这篇博客带你完成:
- RabbitMQ 安装(Docker 一键部署)
- 核心概念(端口、虚拟主机、用户权限)
- HelloWorld 模型(最简单的 1 对 1 消息传递)
- 工作模型(任务分发、能者多劳、持久化)
一、RabbitMQ安装
1.1 Docker安装(推荐)
bash
# 拉取镜像(带管理界面)
docker pull rabbitmq:3.12-management
# 启动容器
docker run -d \
--name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
-e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin123 \
rabbitmq:3.12-management
端口说明:
- 5672: 生产者/消费者通信端口(AMQP 协议)
- 15672: 管理界面端口(HTTP)
访问管理界面:
http://localhost:15672
用户名:admin
密码:admin123
1.2 核心配置
虚拟主机(Virtual Host)
作用: 逻辑隔离,不同项目使用不同的 vhost,互不干扰。
bash
# 创建虚拟主机
docker exec -it rabbitmq rabbitmqctl add_vhost /myapp
# 查看虚拟主机
docker exec -it rabbitmq rabbitmqctl list_vhosts
用户与权限
bash
# 创建用户
docker exec -it rabbitmq rabbitmqctl add_user myuser mypassword
# 设置用户角色(administrator/monitoring/management/policymaker/none)
docker exec -it rabbitmq rabbitmqctl set_user_tags myuser administrator
# 授权用户访问虚拟主机
docker exec -it rabbitmq rabbitmqctl set_permissions -p /myapp myuser ".*" ".*" ".*"
权限说明:
- 第一个
".*":配置权限(exchange、queue 的创建) - 第二个
".*":写权限(发送消息) - 第三个
".*":读权限(消费消息)
1.3 SpringBoot集成
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: admin
password: admin123
virtual-host: / # 默认虚拟主机
二、HelloWorld模型
2.1 模型说明
最简单的 1 对 1 模型:
生产者 → 队列 → 消费者
核心组件:
- Queue: 存储消息的队列
- Producer: 发送消息到队列
- Consumer: 从队列接收消息
2.2 创建队列
java
@Configuration
public class RabbitConfig {
/**
* 创建队列
* durable: 是否持久化(重启后队列还在)
* exclusive: 是否独占(只有当前连接可用)
* autoDelete: 是否自动删除(没有消费者时删除)
*/
@Bean
public Queue helloQueue() {
return new Queue("hello.queue", true); // true = 持久化
}
}
2.3 生产者
java
@Component
public class HelloProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String message) {
// 发送消息到队列
rabbitTemplate.convertAndSend("hello.queue", message);
System.out.println("发送消息: " + message);
}
}
2.4 消费者
java
@Component
@RabbitListener(queues = "hello.queue")
public class HelloConsumer {
@RabbitHandler
public void receiveMessage(String message) {
System.out.println("收到消息: " + message);
}
}
2.5 测试
java
@SpringBootTest
public class RabbitTest {
@Autowired
private HelloProducer helloProducer;
@Test
public void testHelloWorld() throws InterruptedException {
helloProducer.sendMessage("Hello, RabbitMQ!");
helloProducer.sendMessage("Hello, 蓝胖!");
Thread.sleep(1000); // 等待消费者处理
}
}
输出:
发送消息: Hello, RabbitMQ!
发送消息: Hello, 蓝胖!
收到消息: Hello, RabbitMQ!
收到消息: Hello, 蓝胖!
三、工作模型(Work Queues)
3.1 模型说明
任务分发模型: 一个生产者,多个消费者,消息按轮询分发。
┌→ 消费者A
生产者 → 队列 →├→ 消费者B
└→ 消费者C
适用场景: 任务分发、负载均衡。
3.2 代码实现
生产者
java
@Component
public class WorkProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendTask(String task) {
rabbitTemplate.convertAndSend("work.queue", task);
System.out.println("发送任务: " + task);
}
}
消费者
java
@Component
public class WorkConsumer {
@RabbitListener(queues = "work.queue")
public void consumerA(String task) {
System.out.println("消费者A 收到任务: " + task);
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者A 完成任务: " + task);
}
@RabbitListener(queues = "work.queue")
public void consumerB(String task) {
System.out.println("消费者B 收到任务: " + task);
try {
Thread.sleep(2000); // 模拟耗时操作(比A慢)
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("消费者B 完成任务: " + task);
}
}
测试
java
@Test
public void testWorkModel() throws InterruptedException {
for (int i = 1; i <= 4; i++) {
workProducer.sendTask("任务" + i);
}
Thread.sleep(10000);
}
输出(轮询分发):
发送任务: 任务1
发送任务: 任务2
发送任务: 任务3
发送任务: 任务4
消费者A 收到任务: 任务1
消费者B 收到任务: 任务2
消费者A 收到任务: 任务3 (A先完成,继续接收任务3)
消费者B 收到任务: 任务4 (B后完成,接收任务4)
消费者A 完成任务: 任务1
消费者A 完成任务: 任务3
消费者B 完成任务: 任务2
消费者B 完成任务: 任务4
3.3 能者多劳(Fair Dispatch)
问题: 默认轮询分发,不管消费者处理速度。如果消费者A很快(1秒),消费者B很慢(2秒),A会闲置。
解决: 配置 basicQos,每次只发一条消息,处理完再发下一条。
java
@Configuration
public class RabbitConfig {
@Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
// 每次只预取1条消息(能者多劳)
factory.setPrefetchCount(1);
return factory;
}
}
效果:
消费者A 收到任务: 任务1
消费者B 收到任务: 任务2
消费者A 完成任务: 任务1(1秒后)
消费者A 收到任务: 任务3 (A先完成,立即接收任务3)
消费者B 完成任务: 任务2(2秒后)
消费者B 收到任务: 任务4
消费者A 完成任务: 任务3
消费者B 完成任务: 任务4
对比:
| 模式 | 分发策略 | 性能 |
|---|---|---|
| 轮询 | 平均分配 | A 闲置,B 忙碌 |
| 能者多劳 | 按处理能力 | A 处理 3 个,B 处理 1 个 |
3.4 消息持久化
问题: RabbitMQ 重启后,队列中的消息会丢失。
解决: 队列和消息都开启持久化。
队列持久化
java
@Bean
public Queue workQueue() {
return new Queue("work.queue", true); // true = 持久化
}
消息持久化
java
public void sendTask(String task) {
// 设置消息为持久化
MessageProperties props = new MessageProperties();
props.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 持久化
Message message = new Message(task.getBytes(), props);
rabbitTemplate.send("work.queue", message);
}
注意:
- 队列持久化:RabbitMQ 重启后队列还在
- 消息持久化:RabbitMQ 重启后消息还在(需要写入磁盘,性能略降)
- 如果消息丢失,可能是消费者处理失败但没有 ACK
3.5 手动ACK
问题: 消费者处理消息时抛异常,消息会丢失(默认自动 ACK)。
解决: 手动 ACK,处理成功才确认。
yaml
spring:
rabbitmq:
listener:
simple:
acknowledge-mode: manual # 手动ACK
java
@Component
public class WorkConsumer {
@RabbitListener(queues = "work.queue")
public void consumerA(Message message, Channel channel) throws Exception {
String task = new String(message.getBody());
System.out.println("消费者A 收到任务: " + task);
try {
// 处理任务
processTask(task);
// 手动ACK(确认消息已处理)
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("消费者A 完成任务: " + task);
} catch (Exception e) {
// 处理失败,拒绝消息(requeue = true 重新入队)
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
System.out.println("消费者A 处理失败,重新入队: " + task);
}
}
}
ACK 模式对比:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| auto | 自动 ACK(处理完立即确认) | 不关心消息丢失 |
| manual | 手动 ACK(代码中确认) | 关心消息不丢失 |
| none | 不 ACK(消息发出去就不管) | 不推荐 |
四、思维导图速览
RabbitMQ安装与HelloWorld模型
├── 安装
│ ├── Docker部署(rabbitmq:3.12-management)
│ ├── 端口:5672(通信)/ 15672(管理界面)
│ ├── 虚拟主机(vhost):逻辑隔离
│ └── 用户权限:配置/写/读
├── HelloWorld模型(1对1)
│ ├── 生产者:rabbitTemplate.convertAndSend()
│ ├── 消费者:@RabbitListener + @RabbitHandler
│ └── 队列:Queue(durable = 持久化)
├── 工作模型(任务分发)
│ ├── 轮询分发(默认)
│ ├── 能者多劳(basicQos = 1)
│ ├── 消息持久化(MessageDeliveryMode.PERSISTENT)
│ └── 手动ACK(basicAck / basicNack)
└── 关键配置
├── spring.rabbitmq.listener.simple.acknowledge-mode
├── spring.rabbitmq.listener.simple.prefetch
└── @Bean Queue(durable, exclusive, autoDelete)
五、写在最后
学习建议
-
先动手搭建环境
用 Docker 一键部署 RabbitMQ,登录管理界面看看队列、Exchange 的样子,比看文档更直观。
-
HelloWorld 是基础
先跑通最简单的 1 对 1 模型,再学工作模型、发布订阅等高级特性。
-
能者多劳是面试高频题
面试官问你"RabbitMQ 怎么实现负载均衡",直接甩出
basicQos和公平分发,比说"轮询"更有说服力。
高频面试题总结
| 问题 | 核心答案 |
|---|---|
| RabbitMQ的端口有哪些? | 5672(AMQP通信)、15672(管理界面) |
| 什么是虚拟主机(vhost)? | 逻辑隔离,不同项目用不同vhost,互不干扰 |
| HelloWorld模型是什么? | 最简单的1对1模型:生产者→队列→消费者 |
| 工作模型的轮询分发有什么问题? | 不管消费者处理速度,快的闲置,慢的忙碌 |
| 怎么实现能者多劳? | 设置prefetchCount=1,每次只发一条,处理完再发 |
| 如何保证消息不丢失? | 队列持久化 + 消息持久化 + 手动ACK |