经典队列
什么是经典队列
RabbitMQ 经典队列(原始队列类型)是一种通用的队列类型,适用于数据安全不是首要考虑因素的用例,因为存储在经典队列中的数据不会被复制。经典队列使用**非复制**的 FIFO 队列实现。
如果数据安全是首要考虑因素,建议使用仲裁队列和流而不是经典队列。
只要虚拟主机的默认队列类型未被覆盖,经典队列就是默认的队列类型。
经典队列特性
经典队列完全支持队列排他性、队列和消息 TTL(生存时间)、队列长度限制、消息优先级、消费者优先级,并遵守使用策略控制的设置。
与仲裁队列不同,经典队列不支持死信消息处理。经典队列也不支持仲裁队列支持的至少一次死信传递。
每个消费者的 QoS 预取应优先于全局 QoS 预取,即使经典队列支持这两种选项。全局 QoS 预取是一个已弃用的功能,将在**RabbitMQ 4.0**中删除。
虽然可以将经典队列声明为瞬态的,但这使得在升级期间节点重启等情况下难以推断队列的删除,因此不建议使用瞬态队列。对瞬态队列的支持已弃用,并将从**RabbitMQ 4.0**中删除。
从 RabbitMQ 4.0 开始,经典队列是一种非复制的队列类型。仲裁队列和流在需要高可用性和数据安全时提供了更好的替代方案。
经典队列中的持久化(持久存储)
经典队列使用磁盘上的索引来存储磁盘上的消息位置,以及用于持久化消息的消息存储。
除了以下情况外,持久和瞬态消息始终会持久化到磁盘
- 队列被声明为瞬态或消息为瞬态
- 消息小于嵌入阈值(默认为 4096 字节)
- 队列很短(队列最多可以将 2048 条消息保存在内存中,具体取决于消费者传递速率)
通常,除非消息的消费速率足够高,以至于预计内存中的消息将在下一秒内被消费,否则不会将消息保存在内存中。经典队列最多可将 2048 条消息保存在内存中,具体取决于消费者传递速率。较大的消息在必须发送到消费者之前不会读取到内存中。
持久化消息可能被**嵌入**到队列中或发送到**共享消息存储**中。将消息存储在队列中还是在共享消息存储中的决定基于消息的大小(包括标头)。共享消息存储在处理较大的消息方面效率更高,尤其是在这些消息发送到多个队列时。
消息位置写入队列的索引中。每个队列都有一个索引。队列负责跟踪消息位置及其在队列中的位置,并将此信息持久化到索引中。
使用经典队列版本 1 时,嵌入式消息将写入其队列索引;使用经典队列版本 2 时,则写入其**每个队列的消息存储**。
较大的消息将写入共享消息存储。每个虚拟主机有两个这样的存储:一个用于持久化消息,一个用于瞬态消息,但通常将它们一起视为共享消息存储。虚拟主机中的所有队列都使用相同的存储。
经典队列存储实现版本
目前有两个经典队列版本(实现)。根据版本,经典队列将使用不同的消息索引,以及在索引中嵌入小消息方面的工作方式也会有所不同。
经典队列实现版本 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 发行版一起提供的原因是它们都使用原生代码。请注意,此类插件通常会使消息存储运行速度变慢。