11 — 消息队列

核心规律:解耦+削峰+异步,可靠投递三层保障 一句话:MQ是系统间通信的血管,通畅则系统健康,堵塞则系统崩溃


一、核心规律

1.1 MQ三大价值

flowchart LR
    subgraph 解耦["🔗 解耦"]
        J1["生产者无需知道消费者"]
        J2["系统间松耦合"]
        J3["可独立升级扩展"]
    end
    
    subgraph 削峰["📉 削峰"]
        P1["缓冲突发流量"]
        P2["保护下游系统"]
        P3["平滑处理压力"]
    end
    
    subgraph 异步["⚡ 异步"]
        A1["非关键路径异步"]
        A2["提升响应速度"]
        A3["用户体验更好"]
    end
    
    style J1 fill:#e3f2fd
    style P1 fill:#fff3e0
    style A1 fill:#e8f5e9

1.2 选型决策树

flowchart TD
    A["选择MQ?"] --> B{"需要有序性?"}
    B -->|是| C["RabbitMQ"]
    B -->|否| D{"吞吐量要求?"}
    D -->|"高(百万级)"| E["Kafka"]
    D -->|"中(万级)"| F["RabbitMQ"]
    D -->|"低(千级)"| G["Redis Streams"]
    
    H{"需要持久化?"} -->|是| C
    H -->|否| I["Redis Streams"]
    
    style C fill:#e3f2fd
    style E fill:#fff3e0
    style G fill:#e8f5e9

二、RabbitMQ vs Kafka

2.1 核心对比

特性RabbitMQKafka
协议AMQP自定义协议
消息模型队列(点对点)Topic(发布订阅)
吞吐量万级/秒百万级/秒
延迟毫秒级毫秒级
可靠性高(事务+确认)高(副本+刷盘)
消息保留消费后删除可保留多天
适用场景企业级业务消息大数据日志/流处理

2.2 典型应用场景

场景推荐MQ理由
订单状态通知RabbitMQ需要可靠投递、顺序保证
日志收集Kafka高吞吐、持久化
实时推荐Kafka流处理、高吞吐
异步邮件发送RabbitMQ/Redis简单任务、低延迟
用户行为埋点Kafka海量数据、持久化

三、可靠投递保障

3.1 三层保障模型

flowchart TB
    subgraph 生产者端["📤 生产者端"]
        P1["Confirm确认机制"]
        P2["消息持久化"]
        P3["失败重试"]
    end
    
    subgraph MQ服务端["💾 MQ服务端"]
        M1["消息持久化到磁盘"]
        M2["集群副本机制"]
        M3["消息确认ACK"]
    end
    
    subgraph 消费者端["📥 消费者端"]
        C1["手动ACK(业务成功后确认)"]
        C2["死信队列(处理失败)"]
        C3["幂等性设计(防重复)"]
    end
    
    P1 & P2 & P3 --> M1 & M2 & M3
    M1 & M2 & M3 --> C1 & C2 & C3
    
    style P1 fill:#e3f2fd
    style M1 fill:#fff3e0
    style C1 fill:#e8f5e9

3.2 关键实现

// 生产者:Confirm模式
$channel->confirm_select();
$channel->basic_publish($msg, '', 'order_queue');
if (!$channel->wait_for_acks()) {
    // 重试或记录日志
}
 
// 消费者:手动ACK
$channel->basic_consume('order_queue', '', false, false, false, false, function($msg) {
    try {
        // 处理业务逻辑
        processOrder($msg->body);
        $msg->ack();  // 成功后确认
    } catch (Exception $e) {
        $msg->nack(false, true);  // 失败重新入队
    }
});

四、消息顺序性与幂等性

4.1 顺序性保证

顺序性挑战:
├── 并发消费导致乱序
├── 消息重发导致重复
└── 分区消费导致局部有序

解决方案:
├── 单Consumer消费单一Queue(牺牲性能)
├── 消息带序号,消费者排序后处理
└── 按业务Key哈希到固定Queue

4.2 幂等性设计

幂等性 = 同一消息处理多次 = 相同结果

实现方式:
├── 唯一业务ID(数据库唯一索引)
├── 状态机(只有特定状态才能处理)
├── 去重表(记录已处理的消息ID)
└── Redis SETNX(原子操作)
// 幂等性示例:唯一业务ID
public function handleOrder(string $businessId, array $data): void
{
    // 尝试插入,唯一索引保证幂等
    try {
        ProcessedMessage::create([
            'business_id' => $businessId,
            'status' => 'processing',
        ]);
    } catch (UniqueConstraintViolationException $e) {
        // 已处理,忽略
        return;
    }
    
    // 处理业务逻辑...
}

五、本章总结

核心规律:MQ的核心价值是解耦、削峰、异步。可靠投递需要生产者、MQ服务端、消费者三层共同保障。

关键记忆点

  • ✅ RabbitMQ适合企业级业务消息,Kafka适合大数据流处理
  • ✅ 可靠投递 = Confirm + 持久化 + 手动ACK + 幂等
  • ✅ 幂等性是MQ消费方的必备设计
  • ✅ 顺序性需要额外保证,不是MQ的默认特性

延伸阅读