RabbitMQ 核心知识点

文章目录

📋 目录

  1. 核心架构
  2. Exchange交换机类型
  3. 消息可靠性
  4. 消息确认机制
  5. 消息持久化
  6. 死信队列
  7. 延迟队列
  8. 消息堆积处理
  9. 高可用方案
  10. [RabbitMQ vs Kafka vs RocketMQ](#RabbitMQ vs Kafka vs RocketMQ)

1. 核心架构 ⭐⭐⭐⭐⭐

AMQP模型

复制代码
Producer → Exchange → Binding → Queue → Consumer
           (交换机)   (绑定)    (队列)

详细架构:
┌──────────┐
│Producer  │ 发送消息
└────┬─────┘
     ↓ routing key
┌─────────────────────────┐
│  Exchange (交换机)       │
│  - Direct               │
│  - Fanout               │
│  - Topic                │
│  - Headers              │
└────┬────────────────────┘
     ↓ binding (绑定规则)
┌─────────────────────────┐
│  Queue (队列)            │
│  - 存储消息              │
│  - FIFO                 │
└────┬────────────────────┘
     ↓
┌──────────┐
│Consumer  │ 消费消息
└──────────┘

核心概念

java 复制代码
// 1. Virtual Host(虚拟主机)
// 类似数据库,隔离不同应用
vhost: /
vhost: /app1
vhost: /app2

// 2. Exchange(交换机)
// 接收消息并路由到队列
exchange.declare("myExchange", "direct");

// 3. Binding(绑定)
// 连接Exchange和Queue
queue.bind("myExchange", "routing.key");

// 4. Queue(队列)
// 存储消息,FIFO
queue.declare("myQueue");

// 5. Message(消息)
// 包含:properties(属性)+ body(内容)

核心要点

"RabbitMQ是基于AMQP协议的消息中间件,采用Erlang语言开发。

核心架构 包括Producer、Exchange、Binding、Queue、Consumer五个部分。与其他MQ不同的是,RabbitMQ引入了Exchange交换机的概念,Producer不直接发送消息到Queue,而是发送到Exchange,由Exchange根据路由规则将消息分发到不同的Queue。

工作流程:Producer发送消息到Exchange,携带routing key。Exchange根据类型和binding规则,将消息路由到一个或多个Queue。Consumer从Queue获取消息进行消费。

Virtual Host是虚拟主机,类似数据库的概念,不同应用可以使用不同的vhost,实现逻辑隔离。

这种设计的优势是路由灵活,支持多种复杂的消息分发场景,比如直接路由、广播、主题匹配等。"


2. Exchange交换机类型 ⭐⭐⭐⭐⭐

四种Exchange类型

类型 路由规则 使用场景
Direct routing key完全匹配 点对点、精确路由
Fanout 广播,忽略routing key 广播通知
Topic routing key模式匹配 订阅发布、灵活路由
Headers 根据消息头属性匹配 复杂路由(少用)

1. Direct Exchange(直连交换机)

java 复制代码
// 完全匹配routing key

场景:订单处理
Producer发送: routing key = "order.create"
Binding规则: Queue1绑定 "order.create"
           Queue2绑定 "order.pay"
           
结果:消息只发送到 Queue1

代码示例:
// 声明Direct Exchange
channel.exchangeDeclare("direct_exchange", "direct", true);

// 声明队列
channel.queueDeclare("order_create_queue", true, false, false, null);

// 绑定
channel.queueBind("order_create_queue", "direct_exchange", "order.create");

// 发送消息
channel.basicPublish(
    "direct_exchange",    // exchange
    "order.create",       // routing key
    null,
    message.getBytes()
);

特点:

  • ✅ routing key必须完全匹配
  • ✅ 适合点对点消息
  • ✅ 性能最高

2. Fanout Exchange(扇出交换机)

java 复制代码
// 广播模式,忽略routing key

场景:配置更新广播
Producer发送到Fanout Exchange
结果:所有绑定的Queue都会收到消息

┌─────────┐
│Producer │
└────┬────┘
     ↓
┌─────────────┐
│Fanout       │
│Exchange     │
└─┬─────┬─────┬─┘
  ↓     ↓     ↓
Queue1 Queue2 Queue3
  ↓     ↓     ↓
 全收 全收 全收

代码示例:
// 声明Fanout Exchange
channel.exchangeDeclare("fanout_exchange", "fanout", true);

// 绑定多个队列(routing key无意义)
channel.queueBind("queue1", "fanout_exchange", "");
channel.queueBind("queue2", "fanout_exchange", "");
channel.queueBind("queue3", "fanout_exchange", "");

// 发送消息(routing key被忽略)
channel.basicPublish("fanout_exchange", "", null, message.getBytes());

特点:

  • ✅ 忽略routing key
  • ✅ 广播到所有绑定的队列
  • ✅ 适合广播场景

使用场景:

  • 配置更新
  • 缓存刷新
  • 日志收集

3. Topic Exchange(主题交换机)⭐⭐⭐⭐⭐

java 复制代码
// 模式匹配routing key

通配符规则:
*  匹配一个单词
#  匹配0个或多个单词

示例:
routing key: "order.create.success"

binding key: "order.*.*"        → 匹配 ✅
binding key: "order.#"          → 匹配 ✅
binding key: "order.create.*"   → 匹配 ✅
binding key: "*.create.*"       → 匹配 ✅
binding key: "#.success"        → 匹配 ✅
binding key: "order.pay.*"      → 不匹配 ❌

代码示例:
// 声明Topic Exchange
channel.exchangeDeclare("topic_exchange", "topic", true);

// 绑定不同的模式
channel.queueBind("queue1", "topic_exchange", "order.#");          // 所有订单消息
channel.queueBind("queue2", "topic_exchange", "*.create.*");       // 所有创建消息
channel.queueBind("queue3", "topic_exchange", "order.create.*");   // 订单创建消息

// 发送消息
channel.basicPublish(
    "topic_exchange",
    "order.create.success",  // routing key
    null,
    message.getBytes()
);
// 结果:queue1、queue2、queue3都会收到

特点:

  • ✅ 支持模式匹配
  • ✅ 灵活性最高
  • ✅ 最常用的Exchange类型

使用场景:

  • 日志分级(error.#、info.#)
  • 业务分类(order.#、user.#)
  • 复杂路由场景

4. Headers Exchange(头交换机)

java 复制代码
// 根据消息头属性匹配(很少用)

// 声明Headers Exchange
channel.exchangeDeclare("headers_exchange", "headers", true);

// 绑定(指定匹配规则)
Map<String, Object> headers = new HashMap<>();
headers.put("x-match", "all");  // all=全部匹配,any=任意匹配
headers.put("format", "pdf");
headers.put("type", "report");
channel.queueBind("queue1", "headers_exchange", "", headers);

// 发送消息(设置headers)
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .headers(Map.of("format", "pdf", "type", "report"))
    .build();
channel.basicPublish("headers_exchange", "", props, message.getBytes());

Exchange对比总结

Exchange 特点 使用场景 性能
Direct 精确匹配 点对点消息 ⭐⭐⭐⭐⭐
Fanout 广播 广播通知 ⭐⭐⭐⭐⭐
Topic 模式匹配 复杂路由 ⭐推荐 ⭐⭐⭐⭐
Headers 属性匹配 复杂匹配(少用) ⭐⭐⭐

核心要点

"RabbitMQ最大的特点是引入了Exchange交换机,支持四种类型:

Direct Exchange 是精确匹配,routing key必须完全一致才能路由到队列。适合点对点消息,比如订单处理。

Fanout Exchange 是广播模式,会忽略routing key,将消息发送到所有绑定的队列。适合配置更新、缓存刷新等需要广播的场景。

Topic Exchange 是最常用的,支持通配符匹配。用星号(*)匹配一个单词,井号(#)匹配0个或多个单词。比如routing key是'order.create.success',binding key是'order.#'就能匹配。非常灵活,适合日志分级、业务分类等复杂路由场景。

Headers Exchange 根据消息头属性匹配,功能强大但性能较差,实际很少用。

我们项目中主要用Topic Exchange,可以灵活地将不同类型的消息路由到不同的队列处理。"


3. 消息可靠性 ⭐⭐⭐⭐⭐

三个维度保证可靠性

复制代码
1. 生产端可靠性
   ├─ 消息持久化(deliveryMode=2)
   ├─ 发送确认(Confirm机制)
   └─ 事务机制(Transaction)

2. Broker可靠性
   ├─ Queue持久化
   ├─ Exchange持久化
   ├─ 消息持久化
   └─ 镜像队列(集群)

3. 消费端可靠性
   ├─ 手动ACK(manual acknowledge)
   ├─ 消费失败重试
   └─ 死信队列(DLX)

生产端:Confirm确认机制 ⭐⭐⭐⭐⭐

核心原理:

复制代码
Producer发送消息 → Broker接收并持久化 → 返回ACK/NACK给Producer

ACK:消息成功持久化
NACK:消息处理失败

代码实现:

java 复制代码
// 1. 开启Confirm模式
Channel channel = connection.createChannel();
channel.confirmSelect();  // 开启发送确认

// 2. 发送消息
channel.basicPublish(exchange, routingKey, props, message.getBytes());

// 3. 等待确认
if (channel.waitForConfirms()) {
    System.out.println("✓ 消息发送成功");
} else {
    System.err.println("✗ 消息发送失败");
}

// ===== 异步Confirm(推荐)=====
channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) {
        // 消息确认成功
        System.out.println("✓ 消息确认: " + deliveryTag);
    }
    
    @Override
    public void handleNack(long deliveryTag, boolean multiple) {
        // 消息确认失败
        System.err.println("✗ 消息失败: " + deliveryTag);
        // 重试或记录失败日志
    }
});

// 批量发送
for (int i = 0; i < 1000; i++) {
    channel.basicPublish(exchange, routingKey, props, msg.getBytes());
}
// 异步接收确认

生产端:事务机制

java 复制代码
// 1. 开启事务
channel.txSelect();

try {
    // 2. 批量发送消息
    for (int i = 0; i < 10; i++) {
        channel.basicPublish(exchange, routingKey, null, msg.getBytes());
    }
    
    // 3. 提交事务
    channel.txCommit();  // ✅ 所有消息投递成功
    
} catch (Exception e) {
    // 4. 回滚事务
    channel.txRollback();  // ❌ 所有消息回滚
}

对比:

复制代码
Confirm机制:异步确认,性能高,推荐使用 ⭐⭐⭐⭐⭐
事务机制:  同步阻塞,性能低,不推荐使用 ⭐⭐

核心要点

"RabbitMQ从三个维度保证消息可靠性:

生产端可靠性:主要通过Confirm确认机制。开启confirmSelect后,Broker接收到消息并持久化完成会返回ACK给Producer。可以同步等待确认,也可以异步监听确认结果。Confirm机制比事务机制性能高得多,是推荐的方式。事务机制虽然也能保证可靠性,但是同步阻塞,性能很差。

Broker可靠性:通过持久化保证。Exchange、Queue、Message都设置为持久化,即使Broker重启也不会丢失。集群模式下可以使用镜像队列,数据在多个节点备份。

消费端可靠性:使用手动ACK模式,只有业务处理成功才确认消息。如果处理失败可以选择重新入队或拒绝。配合死信队列处理多次失败的消息。

我们项目中用Confirm机制保证生产端可靠性,用手动ACK保证消费端可靠性,整体消息不丢失率达到99.99%。"


4. 消息确认机制 ⭐⭐⭐⭐⭐

消费端ACK机制

java 复制代码
// 1. 自动ACK(不推荐)
channel.basicConsume(queueName, true, consumer);  // autoAck=true
// 消息一接收就确认,处理失败消息会丢失

// 2. 手动ACK(推荐)⭐⭐⭐⭐⭐
channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(String consumerTag, 
                              Envelope envelope,
                              AMQP.BasicProperties properties, 
                              byte[] body) {
        try {
            String message = new String(body);
            processMessage(message);
            
            // ✅ 处理成功,手动确认
            channel.basicAck(
                envelope.getDeliveryTag(),  // 消息标识
                false                        // multiple=false,只确认当前消息
            );
            
        } catch (Exception e) {
            try {
                // ❌ 处理失败,选择处理方式
                
                // 方式1:拒绝并重新入队
                channel.basicNack(
                    envelope.getDeliveryTag(),
                    false,    // multiple
                    true      // requeue=true,重新入队
                );
                
                // 方式2:拒绝且不重新入队(进入死信队列)
                channel.basicReject(
                    envelope.getDeliveryTag(),
                    false     // requeue=false
                );
                
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }
});

ACK的三种操作

java 复制代码
// 1. basicAck - 确认消息
channel.basicAck(deliveryTag, multiple);
// multiple=false: 只确认当前消息
// multiple=true: 批量确认(deliveryTag及之前的所有消息)

// 2. basicNack - 拒绝消息(支持批量)
channel.basicNack(deliveryTag, multiple, requeue);
// requeue=true: 重新入队
// requeue=false: 丢弃或进入死信队列

// 3. basicReject - 拒绝消息(不支持批量)
channel.basicReject(deliveryTag, requeue);
// 只能拒绝单条消息

核心要点

"RabbitMQ的消费端ACK机制分为自动和手动两种。

自动ACK性能高但不可靠,消息一旦投递给Consumer就立即确认,如果处理失败消息会丢失。

手动ACK是推荐方式,只有业务处理成功才调用basicAck确认。如果处理失败,可以调用basicNack拒绝消息并选择是否重新入队。如果重新入队,消息会重新投递;如果不重新入队,消息会进入死信队列。

需要注意的是,basicAck支持批量确认,multiple=true可以确认deliveryTag之前的所有未确认消息,但要小心使用,确保这些消息都处理成功了。

还有一个basicReject方法也是拒绝消息,但只能拒绝单条,basicNack可以批量拒绝,功能更强。

我们项目中都用手动ACK,保证消息可靠消费。"


5. 消息持久化 ⭐⭐⭐⭐⭐

三要素持久化

java 复制代码
// 1. Exchange持久化
channel.exchangeDeclare(
    "my_exchange",
    "direct",
    true,      // durable=true,持久化
    false,
    null
);

// 2. Queue持久化
channel.queueDeclare(
    "my_queue",
    true,      // durable=true,持久化
    false,
    false,
    null
);

// 3. Message持久化
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .deliveryMode(2)  // 2=持久化,1=非持久化
    .build();

channel.basicPublish(exchange, routingKey, props, message.getBytes());

三者必须都持久化,消息才不会丢失!

复制代码
只持久化Queue → Exchange重启后消失,消息无法路由 ❌
只持久化Message → Queue重启后消失,消息丢失 ❌
全部持久化 → Broker重启后都恢复 ✅

核心要点

"RabbitMQ的持久化需要三要素都配置:Exchange、Queue、Message。

Exchange持久化:声明时设置durable=true,Broker重启后Exchange定义不会丢失。

Queue持久化:同样设置durable=true,队列定义会持久化,但队列中的消息是否持久化取决于消息本身。

Message持久化:发送时设置deliveryMode=2,消息会写入磁盘。注意只有持久化的消息才会写入磁盘,非持久化消息只在内存中。

必须三者都持久化才能保证消息不丢失。如果只持久化Queue,消息仍然是非持久化的,Broker重启会丢失。

需要注意的是,持久化会降低性能,因为涉及磁盘IO。对于非重要消息,可以选择非持久化,提高吞吐量。"


6. 死信队列(DLX)⭐⭐⭐⭐⭐

什么是死信队列?

死信(Dead Letter)的三种情况:

  1. 消息被拒绝(basicReject/basicNack)且 requeue=false
  2. 消息过期(TTL超时)
  3. 队列达到最大长度

配置死信队列

java 复制代码
// 1. 声明死信Exchange
channel.exchangeDeclare("dlx_exchange", "direct", true);

// 2. 声明死信Queue
channel.queueDeclare("dlx_queue", true, false, false, null);

// 3. 绑定
channel.queueBind("dlx_queue", "dlx_exchange", "dlx_routing_key");

// 4. 声明业务Queue,指定死信Exchange
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "dlx_exchange");        // 死信交换机
args.put("x-dead-letter-routing-key", "dlx_routing_key"); // 死信routing key
args.put("x-message-ttl", 60000);                          // 消息TTL 60秒

channel.queueDeclare("business_queue", true, false, false, args);

// 工作流程:
// business_queue 中的消息 → 过期/拒绝/队列满
//                          ↓
//                    进入 dlx_exchange
//                          ↓
//                      路由到 dlx_queue

死信队列应用场景

java 复制代码
// 场景1:订单超时自动取消
// 订单创建时发送消息,设置TTL=30分钟
// 30分钟后消息过期进入死信队列
// 死信队列消费者检查订单状态,未支付则取消

// 场景2:消费失败处理
// 消息消费失败多次后进入死信队列
// 人工或后台任务处理死信消息

// 场景3:限流队列
// 队列设置最大长度
// 超出部分进入死信队列,降级处理

核心要点

"RabbitMQ的死信队列是处理异常消息的重要机制。

死信产生的三种情况:一是消息被拒绝且不重新入队,二是消息过期超时,三是队列达到最大长度。

配置方式是在声明业务Queue时,通过参数指定死信Exchange和routing key。当消息变成死信后,会自动发送到死信Exchange,然后路由到死信Queue。

典型应用场景:订单超时取消。创建订单时发送一条消息,设置TTL为30分钟,如果30分钟内没被消费(说明订单未支付),消息会过期进入死信队列,死信消费者检查订单状态并取消未支付的订单。

另一个场景是处理消费失败的消息。如果消息消费失败多次,拒绝且不重新入队,进入死信队列由人工或专门的处理程序来处理。

死信队列是实现延迟消息、超时处理、异常处理的核心机制。"


7. 延迟队列 ⭐⭐⭐⭐⭐

实现原理

复制代码
RabbitMQ本身不支持延迟消息,通过 TTL + 死信队列 实现

流程:
发送消息到延迟队列(设置TTL)
    ↓
消息在延迟队列等待(无Consumer)
    ↓
TTL过期,消息变成死信
    ↓
进入死信Exchange
    ↓
路由到业务Queue
    ↓
Consumer消费

核心代码

java 复制代码
/**
 * 延迟队列实现
 */
public class DelayQueue {
    
    public void setupDelayQueue(Channel channel) throws Exception {
        
        // 1. 声明死信Exchange(接收过期消息)
        channel.exchangeDeclare("business_exchange", "direct", true);
        
        // 2. 声明业务Queue(接收死信消息)
        channel.queueDeclare("business_queue", true, false, false, null);
        channel.queueBind("business_queue", "business_exchange", "business_key");
        
        // 3. 声明延迟Queue(设置TTL和死信Exchange)
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 30000);                    // 30秒后过期
        args.put("x-dead-letter-exchange", "business_exchange"); // 死信交换机
        args.put("x-dead-letter-routing-key", "business_key");   // 死信routing key
        
        channel.queueDeclare("delay_queue", true, false, false, args);
        
        // 4. 声明延迟Exchange
        channel.exchangeDeclare("delay_exchange", "direct", true);
        channel.queueBind("delay_queue", "delay_exchange", "delay_key");
    }
    
    // 发送延迟消息
    public void sendDelayMessage(Channel channel, String message) throws Exception {
        // 发送到延迟队列
        channel.basicPublish(
            "delay_exchange",
            "delay_key",
            MessageProperties.PERSISTENT_TEXT_PLAIN,
            message.getBytes()
        );
        
        System.out.println("发送延迟消息: " + message);
        // 30秒后,消息会自动进入 business_queue
    }
}

多级延迟队列

java 复制代码
// 支持多个延迟时间
// delay_5s_queue:   5秒延迟
// delay_30s_queue:  30秒延迟
// delay_5m_queue:   5分钟延迟
// delay_30m_queue:  30分钟延迟

public void sendDelayMessage(String message, int delaySeconds) {
    String queueName = "delay_" + delaySeconds + "s_queue";
    channel.basicPublish("delay_exchange", queueName, null, message.getBytes());
}

RabbitMQ 3.5.8+ 延迟插件

bash 复制代码
# 安装延迟插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange

# 使用延迟插件
Map<String, Object> args = new HashMap<>();
args.put("x-delayed-type", "direct");

channel.exchangeDeclare("delayed_exchange", "x-delayed-message", true, false, args);

// 发送延迟消息
Map<String, Object> headers = new HashMap<>();
headers.put("x-delay", 5000);  // 延迟5秒

AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .headers(headers)
    .build();

channel.basicPublish("delayed_exchange", routingKey, props, message.getBytes());

核心要点

"RabbitMQ本身不支持延迟消息,但可以通过TTL和死信队列组合实现。

实现原理:创建一个延迟队列,设置消息TTL但不设置Consumer。消息发送到延迟队列后,在队列中等待,TTL过期后变成死信,然后通过死信Exchange路由到真正的业务Queue,业务Consumer才能消费到。这样就实现了延迟效果。

典型场景是订单超时取消:用户下单后发送一条延迟30分钟的消息,30分钟后如果订单还未支付,死信消费者收到消息,执行取消订单逻辑。

缺点是每个延迟时间需要一个队列,不够灵活。如果需要任意时间延迟,可以安装rabbitmq_delayed_message_exchange插件,支持动态设置延迟时间。

另一个方案是使用定时任务扫描数据库,但不如消息方式解耦。"


8. 消息堆积处理 ⭐⭐⭐⭐⭐

堆积原因

复制代码
消费端慢(主要原因80%):
├── 消费逻辑耗时(数据库慢查询、外部调用)
├── Consumer数量不足
├── 消费线程数不足
└── 消费异常频繁

生产端快:
└── 流量突增(大促、营销活动)

Broker问题:
└── 内存不足、磁盘满、网络抖动

应急处理方案

java 复制代码
// 1. 增加Consumer数量(最快)⭐⭐⭐⭐⭐
// 水平扩展,启动更多Consumer实例
// 原来1个Consumer → 扩容到10个Consumer
// 消费速度提升10倍

// 2. 增加每个Consumer的线程数
ExecutorService executor = Executors.newFixedThreadPool(64);  // 增加到64个线程

channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
    @Override
    public void handleDelivery(...) {
        executor.submit(() -> {
            // 多线程并发处理
            processMessage(body);
            channel.basicAck(envelope.getDeliveryTag(), false);
        });
    }
});

// 3. 优化Prefetch(预取数量)
channel.basicQos(100);  // 每次预取100条(默认1条)
// 减少网络交互次数

// 4. 优化消费逻辑
// - 数据库批量操作
// - 外部调用异步化
// - 增加缓存
// - 并行处理

// 5. 临时队列转移(紧急)
// 快速消费堆积消息,转发到新队列
// 修复bug后重新消费

监控告警

java 复制代码
// 使用Management API监控队列堆积
public class QueueMonitor {
    
    public void checkQueueDepth(String queueName) {
        // 调用RabbitMQ Management API
        // GET /api/queues/{vhost}/{queue}
        
        JSONObject queueInfo = httpGet("http://localhost:15672/api/queues/%2F/" + queueName);
        int messageCount = queueInfo.getInt("messages");
        
        System.out.println("队列消息数: " + messageCount);
        
        // 告警
        if (messageCount > 10000) {
            sendAlert("队列堆积超过1万: " + messageCount);
        }
    }
}

核心要点

"消息堆积主要是消费端慢导致的,我们的处理方案:

应急处理:首先快速扩容Consumer实例,这是最快的方法,从1个扩到10个,消费速度立即提升10倍。其次增加每个Consumer的并发线程数,用线程池并行处理消息。调大Prefetch预取数量,一次拉取更多消息,减少网络开销。

优化消费逻辑是治本的方法:数据库改批量操作,外部服务调用异步化,增加缓存减少查询。我们之前有个场景,每条消息都查数据库,加了本地缓存后性能提升了5倍。

预防措施:建立监控告警,堆积超过阈值立即通知。做好流量评估和性能压测,提前知道系统承受能力。重要的是要有限流和降级方案,流量过大时可以拒绝部分请求或延迟处理。

极端情况下可以临时转移消息到新队列,修复bug后重新消费,或者丢弃过期消息。"


9. 高可用方案 ⭐⭐⭐⭐⭐

普通集群(默认)

复制代码
架构:
┌─────────┐  ┌─────────┐  ┌─────────┐
│ Node1   │  │ Node2   │  │ Node3   │
│ Queue1  │  │ Queue2  │  │ Queue3  │
└─────────┘  └─────────┘  └─────────┘
    ↑            ↑            ↑
    └────────────┴────────────┘
       共享元数据,消息不共享

特点:
✅ Exchange、Binding等元数据在所有节点共享
❌ Queue及其消息只在声明的节点上
❌ Node1宕机,Queue1的消息无法消费

镜像队列(高可用)⭐⭐⭐⭐⭐

复制代码
架构:
┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│ Node1 (Master)  │  │ Node2 (Mirror)  │  │ Node3 (Mirror)  │
│ Queue1 (主)     │  │ Queue1 (镜像)   │  │ Queue1 (镜像)   │
│ 消息1、2、3     │─>│ 消息1、2、3     │─>│ 消息1、2、3     │
└─────────────────┘  └─────────────────┘  └─────────────────┘

特点:
✅ Queue在多个节点镜像
✅ 消息在所有镜像节点同步
✅ Master宕机,自动选举新Master
✅ 真正的高可用

配置:
{
  "pattern": "^ha\\.",        // 匹配队列名(以ha.开头)
  "definition": {
    "ha-mode": "all",         // 镜像到所有节点
    "ha-sync-mode": "automatic"
  }
}

或指定节点数:
{
  "ha-mode": "exactly",
  "ha-params": 2              // 镜像到2个节点
}

仲裁队列(Quorum Queue)⭐⭐⭐⭐⭐

java 复制代码
// RabbitMQ 3.8+ 推荐使用仲裁队列
// 基于Raft协议,替代镜像队列

// 声明仲裁队列
Map<String, Object> args = new HashMap<>();
args.put("x-queue-type", "quorum");  // 设置为仲裁队列

channel.queueDeclare("quorum_queue", true, false, false, args);

// 特点:
// ✅ 基于Raft协议,强一致性
// ✅ 自动故障转移
// ✅ 性能优于镜像队列
// ✅ 数据安全性更高

核心要点

"RabbitMQ的高可用方案主要有三种:

普通集群是默认模式,多个节点共享Exchange、Binding等元数据,但Queue及其消息只存在于声明的节点上。某个节点宕机,该节点上的Queue无法使用,不是真正的高可用。

镜像队列是传统的高可用方案。Queue会在多个节点创建镜像,消息会同步到所有镜像节点。Master宕机后自动选举新Master,实现故障转移。可以配置镜像到所有节点或指定数量的节点。缺点是同步复制性能开销大,而且不保证强一致性。

仲裁队列是RabbitMQ 3.8之后推荐的方案,基于Raft协议实现,提供强一致性保证。性能优于镜像队列,数据更安全,是现在生产环境的推荐选择。

我们项目中用的是仲裁队列,3个节点部署,允许1个节点宕机,既保证高可用又兼顾性能。"


10. 三大MQ对比 ⭐⭐⭐⭐⭐

RabbitMQ vs Kafka vs RocketMQ

特性 RabbitMQ Kafka RocketMQ
开发语言 Erlang Scala/Java Java
协议 AMQP 自定义 自定义
TPS 万级 百万级 十万级
延迟 微秒级 毫秒级 毫秒级
消息丢失 低(配置好)
消息重复
顺序消息 不支持 支持(分区) 支持(分区)
延迟消息 支持(TTL+DLX) 不支持 支持(18级)
事务消息 本地事务 不支持 支持
死信队列 支持 不支持 支持
消息回溯 不支持 支持 支持
优先级队列 支持 不支持 不支持
管理界面 强大 一般 较好
社区 活跃 非常活跃 活跃
适用场景 企业应用、复杂路由 大数据、日志收集 互联网、高并发

选型建议

复制代码
RabbitMQ:
✅ 适合:中小规模、企业应用、需要复杂路由
✅ 优势:功能丰富、路由灵活、管理方便
❌ 劣势:性能相对较低、Erlang语言不熟悉

Kafka:
✅ 适合:大数据、日志收集、流处理
✅ 优势:超高吞吐量、消息回溯、消息持久化
❌ 劣势:不支持延迟消息、不支持事务消息

RocketMQ:
✅ 适合:互联网、高并发、分布式事务
✅ 优势:高性能、顺序消息、事务消息、延迟消息
❌ 劣势:路由功能不如RabbitMQ灵活

核心要点

"三大MQ各有特点:

RabbitMQ 基于AMQP协议,Erlang开发,最大特点是路由功能强大,支持Direct、Fanout、Topic、Headers四种Exchange,可以实现复杂的消息路由。功能丰富,有死信队列、优先级队列、延迟队列等。性能在万级TPS,适合企业应用和复杂路由场景。管理界面很强大,运维方便。

Kafka 是高性能的分布式消息系统,主要用于大数据和日志收集。最大优势是吞吐量极高,百万级TPS,支持消息回溯,可以重复消费历史消息。但不支持延迟消息和事务消息,路由功能也相对简单。

RocketMQ 是阿里开源的,专为互联网场景设计。性能十万级TPS,支持顺序消息、延迟消息、事务消息,功能比Kafka丰富。分布式架构,扩展性好。

选型建议:企业应用、需要复杂路由用RabbitMQ;大数据、日志收集用Kafka;互联网高并发、需要分布式事务用RocketMQ。

我们项目从RabbitMQ迁移到RocketMQ,主要是性能和事务消息的需求。"


面试核心总结

必问知识点(按重要性排序)

排名 知识点 核心要点
⭐⭐⭐⭐⭐ Exchange类型 Direct/Fanout/Topic/Headers,使用场景
⭐⭐⭐⭐⭐ 消息可靠性 Confirm机制、持久化、手动ACK
⭐⭐⭐⭐⭐ 死信队列 3种死信情况、配置方式、应用场景
⭐⭐⭐⭐⭐ 延迟队列 TTL+DLX实现原理、延迟插件
⭐⭐⭐⭐⭐ 高可用 普通集群/镜像队列/仲裁队列
⭐⭐⭐⭐ 消息确认 手动ACK、批量确认、重试机制
⭐⭐⭐⭐ 消息堆积 原因、处理方案、预防措施
⭐⭐⭐⭐ 性能优化 异步发送、Prefetch、连接池
⭐⭐⭐⭐ RabbitMQ vs Kafka 性能、功能、场景选型

常见面试问题

Q1: RabbitMQ如何保证消息不丢失?

回答: 从三个维度保证:

  1. 生产端:Confirm确认机制 + 消息持久化
  2. Broker端:Exchange持久化 + Queue持久化 + 消息持久化
  3. 消费端:手动ACK + 失败重试

Q2: 如何实现延迟消息?

回答: 两种方式:

  1. TTL + 死信队列(原生支持)
  2. 延迟插件 rabbitmq_delayed_message_exchange

Q3: 死信队列有什么用?

回答: 三大用途:

  1. 处理消费失败的消息
  2. 实现延迟消息(TTL过期)
  3. 实现消息限流(队列满)

Q4: 消息堆积怎么办?

回答:

  1. 扩容Consumer(最快)
  2. 增加消费线程数
  3. 优化消费逻辑
  4. 调大Prefetch

Q5: RabbitMQ vs Kafka 怎么选?

回答:

  • 复杂路由、企业应用 → RabbitMQ
  • 大数据、日志收集 → Kafka
  • 高并发、分布式事务 → RocketMQ

快速记忆要点

架构设计: 基于AMQP协议,Producer发送到Exchange,Exchange根据路由规则分发到Queue,Consumer从Queue消费,核心是Exchange灵活路由

交换机类型: Direct完全匹配适合点对点,Fanout广播适合通知场景,Topic模式匹配最灵活常用,Headers属性匹配很少使用

可靠保证: 生产端Confirm机制异步确认,Broker端Exchange、Queue、Message三要素持久化,消费端手动ACK精确控制

死信机制: 消息拒绝不重入队、TTL过期超时、队列达到最大长度三种情况触发死信,进入DLX后路由到死信队列统一处理

延迟实现: 通过TTL设置过期时间加死信队列转发实现,或安装延迟插件支持任意时间延迟

高可用: 普通集群只共享元数据,镜像队列在多节点同步消息实现高可用,仲裁队列基于Raft协议提供强一致性是推荐方案

堆积应对: 快速扩容Consumer实例,增加消费并发线程数,调大Prefetch预取批量,优化消费业务逻辑

性能优化: 生产端异步发送提高吞吐,Prefetch批量预取减少交互,连接池复用避免频繁创建,消费端并发处理

MQ对比: RabbitMQ路由功能最强大适合复杂场景,Kafka超高吞吐适合大数据日志,RocketMQ功能性能最均衡适合互联网

相关推荐
炼金士5 小时前
分布式虚拟 Actor 技术在码头生产调度中的应用研究
分布式·dapr·actor·集装箱码头
xuyanqiangCode6 小时前
KAFKA自动修改所有以**开头的主题脚本
分布式·kafka·linq
回家路上绕了弯9 小时前
高并发订单去重:布隆过滤器过滤已存在订单号的实战方案
分布式·后端
Slow菜鸟11 小时前
Java后端常用技术选型 |(三)分布式篇
java·分布式
大飞哥~BigFei14 小时前
RabbitMq消费延迟衰减重试实现思路
java·分布式·rabbitmq
java_logo1 天前
NGINX WEBUI Docker 容器化部署指南
运维·nginx·docker·容器·centos·rabbitmq·运维开发
小泊客1 天前
使用讯飞星火 Spark X1-32K 打造本地知识助手
大数据·分布式·spark·大模型应用·本地知识助手
Ace_31750887761 天前
京东关键字搜索接口逆向:从动态签名破解到分布式请求调度
分布式·python