Skip to content

Stream Queue ​

Stream Queue 是 RabbitMQ 3.9+ 引入的新队列类型,借鉴了 Kafka 的设计理念:消息持久化到磁盘,支持回溯消费。

1. 为什么引入 Stream Queue ​

Classic Queue 和 Quorum Queue 的共同问题:消息被确认后就删除了。如果消费者想重新消费已经处理过的消息,做不到。

Stream Queue 解决了这个问题:

  • 消息持久化到磁盘日志(类似 Kafka 的 Log Segment)
  • 消息不因确认而删除,按保留策略自动清理
  • 支持多消费者独立回溯消费

2. Stream Queue 的存储模型 ​

txt
┌──────────────────────────────────────────────┐
│              Stream Queue                    │
│                                              │
│  ┌─────────────────────────────────────────┐ │
│  │  Segment 0 (已关闭)                      │ │
│  │  [msg0][msg1][msg2]...[msg999]          │ │
│  └─────────────────────────────────────────┘ │
│  ┌─────────────────────────────────────────┐ │
│  │  Segment 1 (当前写入)                    │ │
│  │  [msg1000][msg1001]...                  │ │
│  └─────────────────────────────────────────┘ │
│                                              │
│  每个 Segment 是一个只追加的日志文件             │
│  消息按 offset 索引,支持任意位置读取            │
└──────────────────────────────────────────────┘

3. 核心特性 ​

特性说明
消息保留消息不因确认而删除,按时间/大小保留
回溯消费消费者可以从任意 offset 开始消费
多消费者多个消费者独立消费,互不影响
顺序保证Stream 内消息按 offset 严格有序(日志顺序)
高吞吐追加式日志 + 批量处理,利于高吞吐
不支持死信Stream Queue 不支持 DLX
不支持优先级不支持 x-max-priority

4. 声明与使用 ​

java
// 声明 Stream Queue
Map<String, Object> args = new HashMap<>();
args.put("x-queue-type", "stream");
args.put("x-stream-max-segment-size-bytes", 104857600);   // 每个 Segment 100MB
args.put("x-max-length-bytes", 10737418240L);             // 总大小 10GB
args.put("x-max-age", "7D");                              // 保留 7 天
channel.queueDeclare("event.stream", true, false, false, args);

4.1 保留策略 ​

参数说明
x-stream-max-segment-size-bytes单个 Segment 大小(默认 500,000,000 字节)
x-max-length-bytesStream 总大小
x-max-age消息最大保留时间

保留按 Segment 粒度执行,不是逐条删除,实际保留的数据量和时间可能超过设定边界;x-max-length-bytes 与 x-max-age 可组合使用。

5. 消费模式 ​

Stream 消费通过 x-stream-offset 指定起始位置,且必须先设置 QoS(basicQos),否则会报错。

java
channel.basicQos(100);  // Stream 消费必须设置 QoS

// 从最早的可用消息开始
Map<String, Object> args = new HashMap<>();
args.put("x-stream-offset", "first");
channel.basicConsume("event.stream", false, args, new StreamConsumer(channel));

// 从指定 offset 开始(数值不存在时会落到日志起点或终点)
args.put("x-stream-offset", 1000);
channel.basicConsume("event.stream", false, args, new StreamConsumer(channel));

// 从指定时间开始(Java 客户端用 Date 表示时间戳,基于消息到达时间)
args.put("x-stream-offset", new Date(1788134400000L));  // 2026-08-31 00:00:00 UTC
channel.basicConsume("event.stream", false, args, new StreamConsumer(channel));

// 或使用相对时间间隔(与 x-max-age 相同的单位格式)
args.put("x-stream-offset", "1h");
channel.basicConsume("event.stream", false, args, new StreamConsumer(channel));

x-stream-offset 可选值:first(最早可用消息)、last(最后写入的 chunk)、next(默认,仅消费新消息)、数值 offset、时间戳或相对时间间隔(如 1h)。

5.1 ACK 与消费进度 ​

Stream 的 basicAck 不会删除消息,它只是消费确认,用于推进该消费者的投递。但 ACK 不等于持久化消费位置:AMQP 0-9-1 接口不提供 Broker 端的 offset tracking,应用重启后想从上次位置继续,需要自己持久化 offset 并据此重新指定 x-stream-offset;原生 Stream 协议客户端支持服务端 offset tracking,进度由 Broker 保存。

6. Stream Queue vs 其他队列 ​

维度ClassicQuorumStream
消息保留确认即删除确认即删除按策略保留
回溯消费❌❌✅
高可用镜像(废弃)RaftRaft 复制
吞吐量中中高(实际需基准测试)
死信✅✅❌
优先级✅❌❌

7. 适用场景 ​

场景是否适合
事件溯源(Event Sourcing)✅ 非常适合
日志收集✅ 高吞吐 + 保留策略
消息回溯✅ 原生支持
业务消息(订单/支付)⚠️ 需消费确认与重投递语义时用 Quorum
延迟消息❌ 不支持 DLX
实时推送⚠️ 可以,但 Classic 更简单

8. 与 Kafka 的对比 ​

Stream Queue 的设计理念和 Kafka 类似,但有一些关键区别:

维度Stream QueueKafka
协议AMQP(RabbitMQ 原生)自定义协议
分区单 Stream(Super Stream 支持分区)多 Partition
消费者组AMQP 0-9-1 接口不支持;原生协议支持支持
生态RabbitMQ 生态Kafka Connect/Streams/ksqlDB
运维RabbitMQ 统一管理独立集群

如果只需要"消息保留 + 回溯消费",Stream Queue 够用。如果需要 Kafka 式的流处理生态(Connect/Streams/ksqlDB),再上 Kafka。

Released under the MIT License.