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

0 阅读10分钟

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)

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

生产者 → 队列 → 消费者1
             → 消费者2
             → 消费者3

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

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

3. 发布订阅模式(Fanout)

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

生产者 → Fanout交换机 → 队列1 → 消费者1
                      → 队列2 → 消费者2

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

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

4. 路由模式(Direct)

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

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

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

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

5. 主题模式(Topic)

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

生产者 → 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. 添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2. 配置连接信息

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /

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

3. 发送消息(RabbitTemplate)

@Autowired
private RabbitTemplate rabbitTemplate;

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

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

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

4. 接收消息(@RabbitListener)

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

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

5. 配置队列和交换机(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 交换机(精确匹配):

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

Fanout 交换机(广播):

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

Topic 交换机(通配符):

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

7. 消息确认机制

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

spring:
  rabbitmq:
    publisher-confirm-type: correlated  # 开启确认

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

spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认(更安全)

手动确认代码:

@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. 队列满了,新消息进不来

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

配置方式:

@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 + 死信队列 实现:

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

3. 消息幂等性

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

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

@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 防止重复消费