经典队列
什么是经典队列
RabbitMQ 经典队列(最初的队列类型)是一种通用的队列类型,适用于数据安全不是首要考虑因素的用例,因为经典队列中存储的数据不会被复制。经典队列使用非复制的 FIFO 队列实现。
如果数据安全是首要考虑因素,建议使用 quorum 队列 和 流 而不是经典队列。
只要虚拟主机的默认队列类型未被覆盖,经典队列就是默认队列类型。
经典队列特性
经典队列完全支持 队列独占性、队列和消息 TTL(生存时间)、队列长度限制、消息优先级、消费者优先级,并遵守使用 策略 控制的设置。
与 quorum 队列不同,经典队列不支持 毒消息处理。经典队列也不支持 quorum 队列支持的至少一次死信处理。
即使经典队列同时支持这两种选项,也应优先选择 按消费者 QoS 预取 而不是全局 QoS 预取。全局 QoS 预取是一项已弃用的功能,将在 RabbitMQ 4.0 中移除。
虽然经典队列可以声明为瞬态队列,但这使得在升级期间节点重启等情况下难以推断队列删除的原因,因此不鼓励使用瞬态队列。对瞬态队列的支持已弃用,将在 RabbitMQ 4.0 中移除。
从 RabbitMQ 4.0 开始,经典队列是一种非复制队列类型。Quorum 队列 和 流 在需要高可用性和数据安全时提供了更好的替代方案。
经典队列中的持久性(持久存储)
经典队列使用磁盘索引来存储消息在磁盘上的位置,以及消息存储来持久化消息。
持久消息和瞬态消息 始终会持久化到磁盘,除非在以下情况下:
- 队列被声明为瞬态队列或消息是瞬态消息
- 消息小于嵌入阈值(默认为 4096 字节)
- 队列很短(队列最多可以在内存中保留 2048 条消息,具体取决于消费者传递速率)
通常,消息不会保存在内存中,除非消息的消费速率足够高,以至于预计内存中的消息将在下一秒内被消费。经典队列最多在内存中保留 2048 条消息,具体取决于消费者传递速率。较大的消息不会被读取到内存中,直到它们必须发送给消费者。
持久化消息可以嵌入到队列中或发送到共享消息存储中。将消息存储在队列中还是共享消息存储中的决定取决于消息的大小,包括标头。共享消息存储在处理较大的消息时效率更高,特别是当这些消息发送到多个队列时。
消息位置写入队列的索引中。每个队列都有一个索引。队列负责跟踪消息位置及其在队列中的位置,并将此信息持久化在索引中。
当使用经典队列版本 1 时,嵌入的消息写入其队列索引中;当使用经典队列版本 2 时,写入其每个队列的消息存储中。
较大的消息写入共享消息存储中。每个 vhost 都有两个这样的存储:一个用于持久消息,另一个用于瞬态消息,但它们通常被一起视为共享消息存储。vhost 中的所有队列都使用相同的消息存储。
经典队列存储实现版本
目前有两种经典队列版本(实现)。根据版本,经典队列将使用不同的消息索引,并且在索引中嵌入小消息方面的操作也会有所不同。
经典队列实现版本 1
RabbitMQ 4.0 移除了对经典队列版本 1 的支持。
经典队列实现版本 2
版本 2 中的索引仅使用段文件,并且仅在必要时才从磁盘加载消息。它将根据当前的消费速率加载更多消息。版本 2 不在其索引中嵌入消息,而是使用每个队列的消息存储。
版本 2 在 RabbitMQ 3.10.0 中添加,并在 RabbitMQ 3.12.0 中得到了显著改进。从 RabbitMQ 4.0 开始,不再支持版本 1 队列。
版本 1 到版本 2 的迁移
当 RabbitMQ 4.0 节点启动时,它将自动将任何现有的 v1 队列迁移到 v2(它将重写它们的磁盘表示形式)。
请注意,对于大型队列,转换可能需要一些时间,并导致队列在转换运行时不可用。作为参考,在我们的测试机器上,迁移需要:
- 2 秒迁移 1000 个队列,每个队列包含 1000 条 100 字节的消息
- 9 秒迁移一个包含 100 万条 100 字节消息的队列
- 3 秒迁移一个包含 100 万条 5000 字节消息的队列(默认嵌入大小为 4096 字节,5000 字节消息在消息存储中,因此需要迁移的数据更少)
鉴于上述数字,除非有很多队列包含大量消息,否则迁移应在几秒钟内完成。
可以在升级到 RabbitMQ 4.0 之前执行此迁移。有关详细信息,请参阅 RabbitMQ 3.13 文档。
经典队列的资源使用
经典队列旨在在大多数情况下无需配置即可提供相当不错的吞吐量。但是,有时一些配置是有用的。本节介绍了一些可配置的值,这些值会影响节点的稳定性、吞吐量、延迟和 I/O 特性。除了充分利用您的队列外,还可以考虑习惯使用 PerfTest 进行基准测试。
一些相关信息包括:
经典队列的文件句柄使用
RabbitMQ 服务器可以打开的文件句柄数量有限。每个正在运行的网络连接 都需要一个文件句柄,其余的句柄可供队列使用。
经典队列版本 2 不再像 v1 那样尝试适应少量文件描述符。它们期望服务器配置了大量文件描述符限制,并且始终能够在必要时打开新的文件句柄。索引在任何时候最多保持 4 个文件句柄打开,每个队列的存储保持 1 个文件句柄打开,但在将数据刷新到磁盘时可能会打开另一个。这意味着理论上每个队列需要最多 6 个可用的文件描述符才能正常运行。在实践中,只有繁忙的队列才需要那么多;其他队列使用 3 或 4 个文件句柄就可以正常运行。
由于不使用文件句柄管理子系统,版本 2 不跟踪那么多 I/O 统计信息;仅跟踪读取和写入的数量。其他指标可以在操作系统级别获得。
经典队列的内存占用
经典队列最多可以在内存中保留 2048 条消息,具体取决于消费速率。但是,经典队列将避免过早地从磁盘读取较大的消息。在 RabbitMQ 3.12 中,这意味着大于嵌入阈值(默认情况下为 4096 字节)的消息。
版本 2 中的索引和每个队列的存储都会缓冲条目。就索引而言,这通常不是问题,因为它只跟踪元数据。但是,每个队列的存储默认最多使用 1MB 的内存(写入缓冲区中为 512KB,缓存中为 512KB)。当刷新到磁盘时,存储将首先清除缓存,然后将写入缓冲区中的消息移动到缓存中,从而有效地将缓存中的数据替换为写入缓冲区中的数据。因此,写入缓冲区和缓存的大小是相关的。可以使用高级配置通过 rabbit 的 classic_queue_store_v2_max_cache_size 参数进行配置。
空闲队列将减少其内存使用量。当执行影响许多队列的操作(例如定义新策略)时,这有时会导致令人惊讶的峰值。在这种情况下,队列将需要再次分配更多内存。队列越多,预期的峰值就越大。
共享消息存储需要索引。默认的消息存储索引为存储中的每条消息使用少量内存。
备用消息存储索引实现
如上所述,写入消息存储的每条消息都为其索引条目使用少量内存。消息存储索引在 RabbitMQ 中是可插拔的,其他实现可以作为插件使用,从而消除此限制。
它们未随 RabbitMQ 发行版一起发布的原因是它们都使用本机代码。请注意,此类插件通常会使消息存储运行速度变慢。