RabbitMQ 从入门到实战:三天掌握消息队列核心与应用

RabbitMQ 学习笔记

hi,这些是我整理的rabbitmq相关的笔记,具体怎么安装在这里就不一一阐述了,不知道的可以问一下ai或者搜索相关视频,我将rabbitmq学习分为三天进行,最好对着步骤进行实操,效果更佳。

一、RabbitMQ 是什么

RabbitMQ 是一个消息队列中间件,负责在两个系统之间传递消息。

打个比方:

  • 你去餐厅吃饭,点完单后拿到一个号码牌(消息),后厨按号码牌顺序做菜(消费消息)
  • 你不用站在厨房门口等,可以先去找座位(异步处理)
  • 后厨忙不过来,单子在排队(流量削峰)

核心角色:

角色 作用 举例
生产者(Producer) 发消息的人 用户下单,系统发一条消息
消费者(Consumer) 收消息的人 库存服务收到消息,扣减库存
队列(Queue) 存消息的地方 消息在队列里排队等待处理
交换机(Exchange) 路由消息到队列 根据规则决定消息发给哪个队列

二、为什么需要 RabbitMQ

1. 异步处理

用户下单后,需要:扣库存、发短信、记日志。

不用 RabbitMQ:下单 → 扣库存 → 发短信 → 记日志 → 返回(全部串行,慢)

用 RabbitMQ:下单 → 扣库存 → 发消息到队列 → 返回(快,短信和日志异步处理)

2. 系统解耦

订单系统不直接调用库存系统、短信系统,而是发消息到队列,各系统自己来取消息。

好处:订单系统不需要知道谁在用这个消息,加新系统不用改订单代码。

3. 流量削峰

秒杀活动瞬间涌入大量请求,先全部放进队列排队,后端按自己能力慢慢处理,不会被压垮。


三、RabbitMQ 核心概念

1. 消息流转过程

复制代码
生产者 → 交换机 → 队列 → 消费者

生产者不直接把消息发给队列,而是先发给交换机,交换机再根据规则转发到队列。

2. 交换机类型

类型 特点 场景
Direct 精确匹配路由键 点对点精确路由
Fanout 广播给所有绑定队列 消息广播
Topic 通配符匹配路由键 灵活的消息分类
Headers 按消息头匹配 很少使用

3. 路由键(Routing Key)

消息带的一个标签,交换机根据这个标签决定消息发给哪个队列。

例如:消息路由键是 order.pay,Direct 交换机只转发给绑定了 order.pay 的队列。

4. 绑定(Binding)

队列和交换机之间的连接关系,可以理解为"这个队列订阅了交换机的哪类消息"。


四、管理界面操作

启动 RabbitMQ 后,打开浏览器访问:http://localhost:15672

账号:guest,密码:guest

界面主要功能

页面 功能
Overview 查看整体状态、连接数、消息数量
Connections 查看当前连接
Channels 查看信道(一个连接可以有多个信道)
Exchanges 查看和管理交换机
Queues 查看和管理队列
Admin 用户管理、权限管理

手动创建队列

  1. 点击顶部 Queues 标签
  2. 点击 Add a new queue
  3. Name 填 test_queue
  4. 点击 Add queue

手动发送消息

  1. 进入刚创建的队列 test_queue
  2. 展开 Publish message
  3. Payload 填 hello rabbitmq
  4. 点击 Publish message

手动接收消息

  1. 在队列页面展开 Get messages
  2. 点击 Get Message(s)
  3. 可以看到刚才发送的消息内容

五、五种消息模式

1. 简单模式(Simple)

最基础的模式,一个生产者,一个队列,一个消费者。

复制代码
生产者 → 队列 → 消费者
  • 生产者往队列里发消息
  • 消费者从队列里取消息
  • 一对一,一条消息只被一个消费者处理

2. 工作队列模式(Work Queue)

一个生产者,一个队列,多个消费者。

markdown 复制代码
生产者 → 队列 → 消费者1
             → 消费者2
             → 消费者3

多个消费者竞争消费同一个队列的消息,默认轮询分发:每人一条,平均分配。

适合场景:任务分发,多个工作者抢活干。

3. 发布订阅模式(Fanout)

一个生产者,一个 Fanout 交换机,多个队列,多个消费者。

markdown 复制代码
生产者 → Fanout交换机 → 队列1 → 消费者1
                      → 队列2 → 消费者2

Fanout 交换机把消息广播给所有绑定的队列,每个队列都能收到完整的一份消息。

适合场景:一条消息需要多个系统同时处理(如同时通知库存系统和日志系统)。

4. 路由模式(Direct)

一个生产者,一个 Direct 交换机,多个队列。

go 复制代码
生产者 → Direct交换机 →(路由键=error)→ 队列1(绑定了error)→ 消费者1
                       →(路由键=info) → 队列2(绑定了info) → 消费者2

Direct 交换机根据消息的路由键精确匹配,只转发给路由键匹配的队列。

适合场景:按消息类型分流,错误日志和普通日志分开处理。

5. 主题模式(Topic)

一个生产者,一个 Topic 交换机,多个队列。

css 复制代码
生产者 → Topic交换机 →(路由键=order.pay)→ 队列1(绑定了order.*)→ 消费者1
                                            → 队列2(绑定了order.#)→ 消费者2

Topic 交换机支持通配符匹配:

通配符 含义 示例
* 匹配恰好一个词 order.* 匹配 order.pay,不匹配 order.pay.success
# 匹配零个或多个词 order.# 匹配 order.payorder.pay.success

适合场景:灵活的消息分类,比如按模块和操作进行多级分类。


六、五种模式对比

模式 交换机 消费者数 消息去向
简单模式 无(直连队列) 1个 只有一个消费者收到
工作队列 无(直连队列) 多个 轮询分配,每人一条
发布订阅 Fanout 多个 每个队列都收到一份
路由模式 Direct 多个 只有路由键匹配的队列收到
主题模式 Topic 多个 通配符匹配的队列收到

七、Day 1 小结

  1. RabbitMQ 是消息队列,负责在系统之间异步传递消息
  2. 三个核心作用:异步处理、系统解耦、流量削峰
  3. 消息流转:生产者 → 交换机 → 队列 → 消费者
  4. 四种交换机:Direct(精确)、Fanout(广播)、Topic(通配符)、Headers(很少用)
  5. 五种模式:简单、工作队列、发布订阅、路由、主题

八、Day 2:Spring Boot 集成 RabbitMQ

1. 添加依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置连接信息

yaml 复制代码
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /

virtual-host 是虚拟主机,类似 Redis 的 database,用来隔离不同业务的消息,默认用 /

3. 发送消息(RabbitTemplate)

java 复制代码
@Autowired
private RabbitTemplate rabbitTemplate;

// 发送消息到指定队列(简单模式)
rabbitTemplate.convertAndSend("队列名", "消息内容");

// 发送到交换机(带路由键)
rabbitTemplate.convertAndSend("交换机名", "路由键", "消息内容");

convertAndSend 会自动把 Java 对象转成字节数组发送。

4. 接收消息(@RabbitListener)

java 复制代码
@RabbitListener(queues = "队列名")
public void receiveMessage(String message) {
    System.out.println("收到消息:" + message);
}

加上 @RabbitListener 注解后,只要队列里有消息,Spring 会自动调用这个方法。

5. 配置队列和交换机(Java 配置类)

java 复制代码
@Configuration
public class RabbitMQConfig {

    // 声明队列
    @Bean
    public Queue testQueue() {
        return new Queue("test_queue", true);  // true=持久化
    }

    // 声明 Direct 交换机
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("direct_exchange");
    }

    // 绑定:队列绑定到交换机,指定路由键
    @Bean
    public Binding binding(Queue testQueue, DirectExchange directExchange) {
        return BindingBuilder.bind(testQueue)
                .to(directExchange)
                .with("test_routing_key");
    }
}

这段代码做了三件事:

  1. 创建一个叫 test_queue 的队列
  2. 创建一个叫 direct_exchange 的 Direct 交换机
  3. 把队列绑定到交换机,路由键是 test_routing_key

6. 四种交换机类型代码对比

Direct 交换机(精确匹配):

java 复制代码
@Bean
public DirectExchange directExchange() {
    return new DirectExchange("direct_exchange");
}
// 发送时指定路由键
rabbitTemplate.convertAndSend("direct_exchange", "error", "错误消息");
// 只有绑定了 "error" 的队列能收到

Fanout 交换机(广播):

java 复制代码
@Bean
public FanoutExchange fanoutExchange() {
    return new FanoutExchange("fanout_exchange");
}
// 发送时不需要路由键
rabbitTemplate.convertAndSend("fanout_exchange", "", "广播消息");
// 所有绑定的队列都能收到

Topic 交换机(通配符):

java 复制代码
@Bean
public TopicExchange topicExchange() {
    return new TopicExchange("topic_exchange");
}
// 发送
rabbitTemplate.convertAndSend("topic_exchange", "order.pay", "支付消息");
// 绑定了 "order.*" 和 "order.#" 的队列都能收到

7. 消息确认机制

生产者确认 --- 消息有没有成功到达 RabbitMQ:

yaml 复制代码
spring:
  rabbitmq:
    publisher-confirm-type: correlated  # 开启确认

消费者确认 --- 消费者有没有成功处理消息:

yaml 复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认(更安全)

手动确认代码:

java 复制代码
@RabbitListener(queues = "test_queue")
public void receiveMessage(Message message, Channel channel) throws IOException {
    try {
        // 处理消息
        System.out.println("收到:" + new String(message.getBody()));
        // 确认消息(第二个参数false=只确认当前这条)
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        // 处理失败,拒绝消息(第三个参数true=重回队列)
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
    }
}

两种确认方式对比:

方式 配置 特点
自动确认 acknowledge-mode: auto(默认) 收到就确认,快但可能丢消息
手动确认 acknowledge-mode: manual 处理完再确认,安全但代码复杂

九、Day 2 小结

  1. Spring Boot 集成 :引入 spring-boot-starter-amqp,配置 host/port/username/password
  2. 发送消息rabbitTemplate.convertAndSend(交换机, 路由键, 消息)
  3. 接收消息@RabbitListener(queues = "队列名") 自动监听
  4. 配置类:声明队列、交换机、绑定关系
  5. 四种交换机:Direct 精确、Fanout 广播、Topic 通配符、Headers 很少用
  6. 消息确认:生产者确认 + 消费者手动确认,防止消息丢失

十、Day 3:死信队列 + 延迟队列 + 消息幂等性

1. 死信队列(DLX)

什么是死信? 消息"死了",原因有三种:

  1. 消息被消费者拒绝(basicNack 且 requeue=false)
  2. 消息过期了(设置了 TTL 但没人消费)
  3. 队列满了,新消息进不来

死信队列的作用: 给"死掉"的消息一个归宿,而不是直接丢掉。

配置方式:

java 复制代码
@Bean
public Queue normalQueue() {
    Map<String, Object> args = new HashMap<>();
    // 指定死信交换机
    args.put("x-dead-letter-exchange", "dead_exchange");
    // 指定死信路由键
    args.put("x-dead-letter-routing-key", "dead_routing_key");
    return new Queue("normal_queue", true, false, false, args);
}

@Bean
public Queue deadQueue() {
    return new Queue("dead_queue", true);
}

消息在 normal_queue 里"死了",会自动转到 dead_queue


2. 延迟队列

场景: 用户下单后30分钟未支付,自动取消订单。

RabbitMQ 原生没有延迟队列,用 TTL + 死信队列 实现:

markdown 复制代码
生产者 → 发送带TTL的消息 → 延迟队列(没有消费者)
                              ↓ 30分钟后消息过期(变成死信)
                           死信交换机 → 死信队列 → 消费者(检查订单状态,取消未支付订单)
java 复制代码
// 发送延迟消息(30分钟过期)
rabbitTemplate.convertAndSend("delay_exchange", "order_delay", orderMessage,
    message -> {
        message.getMessageProperties().setExpiration("1800000"); // 30分钟,单位毫秒
        return message;
    });

3. 消息幂等性

问题: 消费者可能收到重复消息(网络重试等原因),导致业务执行两次。

解决方案: 消息带唯一 ID,消费前先用 Redis 判断是否已处理。

java 复制代码
@RabbitListener(queues = "dead_queue")
public void handleMessage(Message message, Channel channel) throws IOException {
    String msgId = message.getMessageProperties().getMessageId();

    // 用 Redis 判断是否已处理过(SETNX:只有第一次能设置成功)
    Boolean result = redisTemplate.opsForValue()
            .setIfAbsent("msg:" + msgId, "1", 24, TimeUnit.HOURS);
    if (result == null || !result) {
        // 已经处理过,直接确认,跳过
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        return;
    }

    try {
        // 首次处理,执行业务逻辑
        processOrder(message);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    } catch (Exception e) {
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
    }
}

setIfAbsent 相当于 Redis 的 SETNX,只有第一次能设置成功返回 true,之后都返回 false,从而保证消息只处理一次。


4. 常见问题排查

问题 原因 解决方案
消息堆积 消费者处理太慢 增加消费者数量、提高消费速度
消息丢失 没有确认机制或没持久化 开启确认机制 + 队列和消息都持久化
消费者挂了 自动确认模式下消息被丢失 改用手动确认,未确认的消息重回队列
重复消费 网络重试导致消息被发两次 消息幂等性(Redis SETNX 去重)

十一、三天学习总结

核心知识点

知识点 内容
消息队列作用 异步处理、系统解耦、流量削峰
消息流转 生产者 → 交换机 → 队列 → 消费者
交换机类型 Direct 精确、Fanout 广播、Topic 通配符
Spring Boot 集成 starter-amqp + RabbitTemplate + @RabbitListener
消息确认 生产者确认 + 消费者手动确认
死信队列 处理失败/过期/队列满的消息
延迟队列 TTL + 死信队列实现订单超时取消
消息幂等性 Redis SETNX 防止重复消费
相关推荐
字节逆旅3 小时前
有哪些工作不可以交给AI
人工智能·程序员
程序员cxuan1 天前
Loop 还没玩明白,Graph Engineering 又火了。
人工智能·后端·程序员
谢文峰1 天前
Graph Engineering 是新范式,还是 AI 圈的又一个新词?
程序员
程序员海军1 天前
一个 AI 应用开发程序员的一天,都在屏幕前忙些什么?
前端·后端·程序员
程序员cxuan1 天前
Opus 5 深夜炸场,价格还挺香。。。
人工智能·后端·程序员
用户852495071841 天前
React 组件化思想实战:用 Todo App 讲透“谁管数据,谁管界面”
程序员
码栈研说2 天前
Go 语言大白话入门 10 - 排序与常用数据操作
后端·程序员
小阿鑫2 天前
一个 AI 应用开发程序员的一天,都在屏幕前忙些什么?
ai·程序员·agent·rd270q·明基rd270q
猿的天空2 天前
机器人双手迎来全栈训练系统:灵初智能EgoSteer让灵巧手无所不能
网络·人工智能·计算机·ai·程序员·机器人·编程