深入解析:AMQP协议——(9)Streams 队列

RabbitMQ 中的流队列概述
流队列(通常简称为“流”)是 RabbitMQ 3.9 中引入的一种特殊队列类型,它被设计为一种持久化、可复制、仅追加的数据结构,用于处理高吞吐量消息传递,并支持非破坏性消费。与传统队列不同,流模拟了一个不可变的消息日志,可以多次读取而无需删除,因此非常适合需要消息重放、大量积压消息以及高效扇出的场景。流旨在补充经典队列和仲裁队列,以弥补流式工作负载在性能和可扩展性方面的不足。流使用专用的二进制协议以实现最佳效率,但也支持 AMQP 0-9-1 和 MQTT 客户端,但存在一些限制
目的
流(Stream)专为需要持久缓冲并重复使用消息的应用而设计,例如时间序列数据、事件溯源或审计日志。它们通过复制和磁盘持久化实现高持久性,同时支持高摄取速率和低延迟读取。流并非其他队列的替代品,而是扩展了 RabbitMQ 的功能,使其适用于大规模扇出(将相同的消息传递给多个订阅者)和“时间旅行”(重放历史数据)等用例。
主要特点
- 持久性和耐用性 :始终持久;消息以段文件的形式存储在磁盘上,没有临时队列选项。
- 复制 :采用领导者-跟随者模型,通过手动管理副本来实现容错。
- 非破坏性语义 :消息在被消费后不会被删除;消费者可以从任何偏移量重复读取。
- 保留策略 :使用 x-max-length-bytes (例如,以字节为单位的最大队列大小)和 x-max-age (例如,基于时间的过期时间,如“7d”表示 7 天)来控制数据大小。
- 偏移跟踪 :消费者通过 x-stream-offset 指定起始点(例如,“第一个”、“最后一个”时间戳或数字偏移量)。
- 去重 :使用唯一的生产者名称和顺序发布 ID 进行可选的生产者端过滤。
- 超级流 :用于水平扩展的分区流,与单个活动消费者 (SAC) 集成以实现有序处理。
- 性能 :针对高吞吐量(例如,在合适的硬件上每秒处理数百万条消息)进行了优化,同时最大限度地减少了 RAM 使用量。
- 过滤 :客户端过滤器,用于高效地使用子集(例如,按元数据)。
它们是如何运作的
流式日志系统采用追加式日志机制:生产者将消息追加到日志末尾,消息以块的形式存储在磁盘上固定大小的段文件中。领导节点负责处理写入操作,并将数据同步复制到跟随节点。如果领导节点发生故障,且存在法定人数,则会选举出新的领导节点。保留策略会根据段的大小或存在时间评估并删除旧段,从而确保流不会无限增长。
消费者订阅并从指定的偏移量读取数据,并通过确认信息推进读取位置。读取操作是非破坏性的,允许多个消费者独立访问相同的数据。对于超级流,消息会被分区(例如,通过键哈希),消费者可以针对特定分区进行负载均衡。
数据安全依赖于操作系统级别的同步(无需对每条消息进行显式的 fsync 操作),并通过复制提供冗余。流式传输使用二进制协议来实现压缩和批处理等功能,但为了兼容性,会回退到 AMQP 协议。
与其他队列类型的区别
与经典队列(瞬态/持久化,具有破坏性读取)和法定人数队列(基于 Raft 以实现高安全性)相比,流在设计和权衡方面存在根本差异:
| 特征 | 经典队列 | 谁的队列 | 流队列 |
|---|---|---|---|
| 消费语义学 | 破坏性(确认后删除) | 破坏性的 | 非破坏性(可重放) |
| 持久性 | 可选(持久/短暂) | 始终耐用 | 始终耐用 |
| 复制 | 可选(镜像,已弃用) | 筏式共识,自动 | 领导者-跟随者,手动 |
| 保留 | TTL,最大长度 | 最大长度,溢出 | 按体型/年龄划分的细分市场 |
| 吞吐量 | 缓和 | 高标准,但注重安全 | 积压量大时,费用非常高 |
| 支持的功能 | 优先权、豪华版、独家性 | 中毒处理及优先事项 | 去重、偏移量、过滤器 |
| 用例聚焦 | 一般信息 | 高安全性交易 | 流媒体、重播、扇出 |
| 局限性 | 积压工作占用大量内存 | 不进行非破坏性读取 | 无优先级、DLX 或 TTL |
流媒体优先考虑吞吐量和重放功能,而忽略死信或优先级等功能,因此不太适合需要这些功能的工作流程。
配置示例
- 声明基本流(使用 pika 的 Python) :
Python
import pika connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() arguments = { 'x-queue-type': 'stream', 'x-max-length-bytes': 20000000000, # 20 GB max size 'x-stream-max-segment-size-bytes': 100000000 # 100 MB per segment } channel.queue_declare(queue='my_stream', durable=True, arguments=arguments) - 使用偏移量进行消费(Java RabbitMQ 客户端) :
Java
Mapargs = new HashMap<>(); args.put("x-stream-offset", "last"
浙公网安备 33010602011771号