跳至主要内容
版本:3.13

仲裁队列

概述

RabbitMQ 仲裁队列是一种现代队列类型,它基于 Raft 共识算法 实现了一个持久、复制的 FIFO 队列。

仲裁队列旨在更安全,并提供更简单、定义明确的故障处理语义,使用户在设计和操作其系统时更容易理解。

仲裁队列和 现在取代了原始的、复制的 镜像经典队列。镜像经典队列 现已弃用,并计划移除。使用 将您的 RabbitMQ 镜像经典队列迁移到仲裁队列 指南来迁移当前使用经典镜像队列的 RabbitMQ 安装。

仲裁队列针对 一组用例进行了优化,在这些用例中,数据安全 是重中之重。这在 动机 中有介绍。仲裁队列应被视为复制队列类型的默认选项。

仲裁队列还具有重要的 行为差异 和一些 限制,与经典镜像队列相比,包括工作负载特定的限制,例如当消费者 重复重新排队同一条消息 时。

某些功能,例如 毒性消息处理,是仲裁队列特有的。

对于那些可以从复制和可重复读取中受益的情况, 可能比仲裁队列更合适。

涵盖的主题

本信息涵盖的主题包括

等等。

了解 RabbitMQ 集群 的基本知识将有助于您更好地了解仲裁队列。

动机

仲裁队列采用了不同的复制和共识协议,并放弃了对某些“瞬态”性质功能的支持,这会导致一些限制。这些限制将在本信息中后面介绍。

仲裁队列通过了 重构和更严格的版本原始 Jepsen 测试。这确保了它们在网络分区和故障情况下按预期运行。新的测试会持续运行以发现可能的回归,并定期增强以测试新功能(例如 死信)。

什么是仲裁?

如果有意简化,仲裁 在分布式系统中可以定义为大多数节点之间的一致性((N/2)+1,其中 N 是系统参与者的总数)。

当应用于 RabbitMQ 集群 中的队列镜像时,这意味着大多数副本(包括当前选出的队列领导者)都同意队列及其内容的状态。

仲裁队列与经典镜像队列的差异

仲裁队列与 RabbitMQ 中其他类型的 队列 具有许多基本原理。但是,它们更加专为特定目的而构建,侧重于数据安全和可预测的恢复,并且不支持某些功能。

差异 本指南中介绍。

RabbitMQ 中的经典镜像队列存在技术局限性,这使得难以提供可理解的保证和清晰的故障处理语义。

某些故障情况可能导致镜像队列过早确认消息,从而可能导致数据丢失。

与普通队列的功能比较

仲裁队列与其他 队列 类型共享大多数基本原理。可以使用普通镜像队列的客户端库将能够使用仲裁队列。

以下操作对仲裁队列和普通队列的执行方式相同

某些队列操作存在细微差异

  • 声明
  • 设置消费者的预取

某些功能目前不受仲裁队列支持。

功能矩阵

功能经典队列仲裁队列
非持久队列
排他性
每消息持久性每消息始终
成员资格更改自动手动
消息 TTL(生存时间)是 (自 3.10 起)
队列 TTL部分(租赁不会在队列重新声明时更新)
队列长度限制是(x-overflow 除外:reject-publish-dlx
延迟行为始终(自 3.10 起)
消息优先级
单个活动消费者
消费者排他性否(使用 单个活动消费者
消费者优先级
死信交换机
符合 策略是(参见 策略支持
毒性消息处理
全局 QoS 预取

现代仲裁队列还为许多工作负载提供了 更高的吞吐量和更低的延迟变化

非持久队列

经典队列可以 非持久。仲裁队列始终是持久化的,因为它们假设的 用例 是这样的。

排他性

排他队列 与声明它们的连接的生命周期绑定。仲裁队列的设计是复制和持久的,因此排他属性在它们的上下文中没有意义。因此仲裁队列不能是排他性的。

仲裁队列不适合用作 临时队列

队列和每消息 TTL(自 RabbitMQ 3.10 起)

仲裁队列支持 队列 TTL 和消息 TTL(包括 队列中的每队列消息 TTL发布者中的每消息 TTL)。使用任何形式的消息 TTL 时,内存开销将增加每消息 2 字节。

长度限制

仲裁队列支持 队列长度限制

drop-headreject-publish 溢出行为受支持,但它们不支持 reject-publish-dlx 配置,因为仲裁队列采用了与经典队列不同的实现方法。

reject-publish 溢出行为的当前实现不严格执行限制,并允许仲裁队列至少超过其限制一条消息,因此在需要精确限制的场景中应谨慎使用。

当仲裁队列达到最大长度限制且配置了 reject-publish 时,它会通知每个发布通道,这些通道从那时起将拒绝所有消息返回给客户端。这意味着仲裁队列可能会超过其限制少量消息,因为在通知通道时可能存在正在传输的消息。队列接受的额外消息数量将根据通道通知时正在传输的消息数量而有所不同。

死信

仲裁队列支持 死信交换机(DLX)。

传统上,在集群环境中使用 DLX 并不 安全

自 RabbitMQ 3.10 起,仲裁队列支持更安全的死信形式,该形式对队列之间消息传输使用 至少一次 保证(具有以下限制和注意事项)。

这是通过实现一个特殊的内部死信消费者进程来完成的,该进程类似于具有手动确认的普通队列消费者,除了它只消费已被死信的消息。

这意味着源仲裁队列将保留死信消息,直到它们被确认。内部消费者将消费死信消息,并使用发布者确认将其发布到目标队列。它只有在收到发布者确认后才会确认,从而提供至少一次保证。

最多一次仍然是仲裁队列的默认死信策略,对于死信消息更多是信息性质并且在队列之间传输时丢失并不重要的场景非常有用,或者当下面概述的溢出配置限制不适用时。

启用至少一次死信

要为源仲裁队列启用或打开至少一次死信,请应用以下所有策略(或以x-开头的等效队列参数)

  • dead-letter-strategy设置为at-least-once(默认值为at-most-once)。
  • overflow设置为reject-publish(默认值为drop-head)。
  • 配置dead-letter-exchange
  • 打开功能标志stream_queue(在 3.9 或更高版本中创建的 RabbitMQ 集群中默认打开)。

建议另外配置max-lengthmax-length-bytes,以防止源仲裁队列中消息过度堆积(请参阅下面的注意事项)。

可选地,配置dead-letter-routing-key

限制

即使没有设置队列长度限制,至少一次死信也不适用于默认的drop-head溢出策略。因此,如果配置了drop-head,死信将回退到最多一次。使用溢出策略reject-publish代替。

注意事项

至少一次死信将需要更多系统资源,例如内存和 CPU。因此,只有在死信消息不应丢失的情况下才打开至少一次

至少一次保证开启了一些需要处理的特定故障案例。由于死信消息现在由源仲裁队列保留,直到它们被死信目标队列安全地接受,这意味着它们必须对队列资源限制做出贡献,例如最大长度限制,以便队列可以拒绝接受更多消息,直到某些消息被删除。理论上,如果目标死信队列长时间无法接收消息,而正常队列消费者消费了大部分消息,那么队列就有可能只包含死信消息。

死信消息被认为是“活动”的,直到它们被死信目标队列确认。

死信消息不会及时从源队列中删除,有以下几种情况

  • 配置的死信交换不存在。
  • 消息无法路由到任何队列(相当于mandatory消息属性)。
  • 一个(可能多个)路由的目标队列未确认消息的接收。当目标队列不可用或目标队列拒绝消息(例如,由于队列长度限制超过)时,这种情况可能会发生。

如果出现上述任何情况,死信消费者进程将定期重试,这意味着可能在 DLX 目标队列中出现重复的消息。

对于每个启用了至少一次死信的仲裁队列,将有一个内部死信消费者进程。内部死信消费者进程位于仲裁队列领导者节点上。它将所有死信消息体保存在内存中。它使用 32 个消息的预取大小来限制在没有从目标队列收到确认的情况下保存在内存中的消息体数量。

如果需要高死信吞吐量(每秒数千条消息),可以在高级配置文件rabbit应用程序部分的dead_letter_worker_consumer_prefetch设置中增加该预取大小。

对于源仲裁队列,可以动态地将死信策略从最多一次切换到至少一次,反之亦然。如果死信策略被更改,无论是直接从至少一次更改为最多一次,还是间接更改,例如通过将溢出从reject-publish更改为drop-head,任何尚未被所有目标队列确认的死信消息都将被删除。

发布到源仲裁队列的消息将保存在磁盘上,无论消息传递模式(瞬态或持久性)如何。但是,由源仲裁队列死信的消息将保留原始的消息传递模式。这意味着如果目标队列中的死信消息应该在代理重启后存活,则目标队列必须是持久的,并且在发布消息到源仲裁队列时,消息传递模式必须设置为持久性。

延迟模式

仲裁队列将其消息内容存储在磁盘上(根据 Raft 要求),并且仅在内存中保留每个消息的小型元数据记录。这与先前版本的仲裁队列不同,在先前版本的仲裁队列中,可以选择将消息体保存在内存中。这从未被证明是有益的,尤其是在队列长度很大的情况下。

内存限制配置仍然允许,但没有影响。现在唯一的选项实际上与配置相同:x-max-in-memory-length=0

延迟模式配置不适用。

全局 QoS

仲裁队列不支持全局QoS 预取,其中通道为使用该通道的所有消费者设置单个预取限制。如果尝试从具有激活的全局 QoS 的通道消费仲裁队列,则将返回通道错误。

使用每个消费者的 QoS 预取,这是几个流行客户端中的默认设置。

优先级

仲裁队列支持消费者优先级,但不支持消息优先级

要使用仲裁队列对消息进行优先级排序,请使用多个队列;每个优先级一个。

中毒消息处理(处理重复重新传递)

与经典队列不同,仲裁队列支持中毒消息处理

策略支持

仲裁队列可以通过 RabbitMQ 策略进行配置。下表总结了它们所遵循的策略键。

定义键类型
max-length数字
max-length-bytes数字
overflow"drop-head" 或 "reject-publish"
expires数字(毫秒)
dead-letter-exchange字符串
dead-letter-routing-key字符串
max-in-memory-length数字
max-in-memory-bytes数字
delivery-limit数字

用例

仲裁队列是专门设计而成的。它们并非设计用于解决所有问题。它们的目标用途是用于队列长期存在并且对系统操作的某些方面至关重要的拓扑结构,因此容错和数据安全比最低延迟和高级队列功能更重要。

例如,销售系统中的传入订单或选举系统中投出的选票,其中可能丢失消息会对系统正确性和功能产生重大影响。

股票行情和即时消息系统从仲裁队列中受益较少或根本没有受益。

发布者应该使用发布者确认,因为这是客户端与仲裁队列共识系统交互的方式。只有在已将发布的消息成功复制到仲裁节点并且在系统上下文中被认为是“安全”的情况下,才会发出发布者确认。

消费者应该使用手动确认,以确保未成功处理的消息被返回到队列,以便其他消费者可以重新尝试处理。

何时不使用仲裁队列

在某些情况下,不应使用仲裁队列。它们通常涉及

  • 队列的临时性质:瞬态或专有队列,高队列 churn(声明和删除率)
  • 最低延迟:底层共识算法由于其数据安全特性而具有固有的更高延迟
  • 当数据安全不是优先事项时(例如,应用程序不使用手动确认并且不使用发布者确认)
  • 非常长的队列积压(可能是更好的选择)

用法

如前所述,仲裁队列与其他队列类型共享大部分基础知识。能够指定可选队列参数的客户端库将能够使用仲裁队列。

首先,我们将介绍如何声明仲裁队列。

声明

要声明仲裁队列,请将x-queue-type队列参数设置为quorum(默认值为classic)。此参数必须由客户端在队列声明时提供;它不能使用策略设置或更改。这是因为策略定义或适用的策略可以动态更改,但队列类型不能。它必须在声明时指定。

使用设置为quorumx-queue-type参数声明队列将声明一个仲裁队列,最多具有五个副本(默认复制因子),每个集群节点一个。

例如,一个由三个节点组成的集群将有三个副本,每个节点一个。在一个由七个节点组成的集群中,五个节点将分别拥有一个副本,但两个节点不会托管任何副本。

声明后,仲裁队列可以像任何其他 RabbitMQ 队列一样绑定到任何交换。

如果使用管理 UI声明,则必须使用队列类型下拉菜单指定队列类型。

仲裁队列的客户端操作

以下操作对于仲裁队列的工作方式与经典队列相同

某些队列操作存在细微差异

仲裁队列复制和数据局部性

声明仲裁队列时,必须在集群中启动其初始副本数量。默认情况下,要启动的副本数量最多为三个,每个 RabbitMQ 节点一个。

对于一个仲裁队列来说,最少需要三个节点才能保证副本数量。在拥有更多节点的 RabbitMQ 集群中,添加超过仲裁(多数)数量的副本并不会提高仲裁队列可用性,反而会消耗更多的集群资源。

因此,仲裁队列建议的副本数量为集群节点的仲裁数量(不少于三个)。前提是集群至少拥有三个节点并且已完全形成。

控制初始副本因子

例如,一个拥有三个节点的集群将会拥有三个副本,每个节点一个。在一个拥有七个节点的集群中,三个节点将拥有一个副本,而其余四个节点不会托管任何新声明队列的副本。

与经典镜像队列一样,可以为仲裁队列配置副本因子(队列拥有的副本数量)。

实际意义上,最小因子值为三。强烈建议因子为奇数。这样可以计算出明确的仲裁(多数)节点。例如,在一个拥有两个节点的集群中,不存在“多数”节点。下面“容错性和在线副本的最小数量”部分将以更多示例对此进行说明。

对于更大的集群或拥有偶数个节点的集群,这可能不理想。为了控制仲裁队列成员的数量,在声明队列时设置 x-quorum-initial-group-size 队列参数。提供的组大小参数应为大于零且小于或等于当前 RabbitMQ 集群大小的整数。仲裁队列将启动并运行在声明时集群中存在的 RabbitMQ 节点的一个随机子集上。

如果在所有集群节点加入集群之前声明了一个仲裁队列,并且初始副本数量大于集群成员总数,则使用的实际值将等于集群节点总数。当更多节点加入集群时,副本数量不会自动增加,但可以由操作员增加

队列领导者位置

每个仲裁队列都拥有一个主副本。该副本称为队列领导者。所有队列操作首先通过领导者,然后复制到跟随者(镜像)。这是为了保证消息的 FIFO 排序。

为了避免集群中某些节点托管大部分队列领导者副本,从而处理大部分负载,队列领导者应该在集群节点之间合理均匀地分布。

当声明一个新的仲裁队列时,将随机选择托管其副本的节点集,但始终包含声明队列的客户端连接到的节点。

可以使用三种选项来控制哪个副本成为初始领导者

  1. 设置 queue-leader-locator 策略 键(推荐)
  2. 配置文件中定义 queue_leader_locator 键(推荐)
  3. 使用 x-queue-leader-locator 可选队列参数

支持的队列领导者定位器值有

  • client-local:选择声明队列的客户端连接到的节点。这是默认值。
  • balanced:如果总队列数(经典队列、仲裁队列和流)少于 1000 个,则选择托管仲裁队列领导者数量最少的节点。如果总队列数超过 1000 个,则选择一个随机节点。

管理副本

仲裁队列的副本由操作员显式管理。当一个新节点加入集群时,它不会托管任何仲裁队列副本,除非操作员将其显式添加到仲裁队列或一组仲裁队列的成员(副本)列表中。

当一个节点需要退役(永久从集群中移除)时,必须将其从其当前托管副本的所有仲裁队列的成员列表中显式移除。

提供了一些CLI 命令来执行上述操作

rabbitmq-queues add_member [-p <vhost>] <queue-name> <node>
rabbitmq-queues delete_member [-p <vhost>] <queue-name> <node>
rabbitmq-queues grow <node> <all | even> [--vhost-pattern <pattern>] [--queue-pattern <pattern>]
rabbitmq-queues shrink <node> [--errors-only]

为了成功添加和移除成员,集群中必须有仲裁数量的副本可用,因为集群成员资格更改被视为队列状态更改。

需要注意的是,不要在执行涉及成员资格更改的维护操作时意外地导致队列不可用,从而丢失仲裁。

更换集群节点时,更安全的方法是先添加一个新节点,然后再退役其替换的节点。

为仲裁队列重新平衡副本

一旦声明,RabbitMQ 仲裁队列领导者可能在 RabbitMQ 集群中分布不均匀。要重新平衡,请使用 rabbitmq-queues rebalance 命令。重要的是要了解这不会改变仲裁队列所跨越的节点。要修改成员资格,请参见管理副本

# rebalances all quorum queues
rabbitmq-queues rebalance quorum

可以重新平衡按名称选择的队列子集

# rebalances a subset of quorum queues
rabbitmq-queues rebalance quorum --queue-pattern "orders.*"

或者特定虚拟主机中的仲裁队列

# rebalances a subset of quorum queues
rabbitmq-queues rebalance quorum --vhost-pattern "production.*"

仲裁队列行为

仲裁队列依赖于称为 Raft 的共识协议来确保数据一致性和安全性。

每个仲裁队列都拥有一个主副本(在 Raft 术语中称为领导者)和零个或多个辅助副本(称为跟随者)。

当集群首次形成时,以及后来当领导者变得不可用时,会选举出领导者。

仲裁队列的领导者选举和故障处理

仲裁队列要求声明的节点中必须有仲裁数量的节点可用才能正常工作。当托管仲裁队列的领导者的 RabbitMQ 节点故障或停止时,托管该仲裁队列的其中一个跟随者的另一个节点将被选举为领导者,并恢复操作。

故障和重新加入的跟随者将与领导者重新同步(“追赶”)。与经典镜像队列相比,临时副本故障不需要从当前选定的领导者进行完全重新同步。如果重新加入的副本落后于领导者,则只会传输增量。这个“追赶”过程不会影响领导者可用性。

除了最初的副本集选择之外,必须将副本显式添加到仲裁队列。当添加一个新副本时,它将从领导者同步整个队列状态,类似于经典镜像队列。

容错性和在线副本的最小数量

共识系统可以提供某些关于数据安全性的保证。这些保证意味着在这些保证变得相关之前,需要满足某些条件,例如需要至少三个集群节点才能提供容错性,以及需要超过一半的成员可用才能正常工作。

可以表格形式描述不同大小集群的容错性特性

集群节点数量可容忍的节点故障数量对网络分区容错
10不适用
20
31
41如果一侧存在多数,则可以
52
62如果一侧存在多数,则可以
73
83如果一侧存在多数,则可以
94

如上表所示,拥有少于三个节点的 RabbitMQ 集群无法完全从仲裁队列保证中受益。拥有偶数个 RabbitMQ 节点的 RabbitMQ 集群无法从仲裁队列成员分布在所有节点上中受益。对于这些系统,仲裁队列大小应限制在更小的奇数个节点上。

对于大于 5 的仲裁队列节点大小,性能会急剧下降。我们不建议在超过 7 个 RabbitMQ 节点上运行仲裁队列。默认的仲裁队列大小为 3,可以使用 x-quorum-initial-group-size 队列参数进行控制。

仲裁队列提供的數據安全

仲裁队列旨在在网络分区和故障情况下提供数据安全。只要托管仲裁队列的 RabbitMQ 节点中至少有一半没有永久不可用,使用发布者确认功能成功确认回发布者的消息就不会丢失。

一般来说,仲裁队列优先考虑数据一致性而不是可用性。

对于尚未使用发布者确认机制确认的消息,不提供任何保证。这些消息可能在“中途”丢失,在操作系统缓冲区中丢失,或者以其他方式无法到达队列领导者。

仲裁队列可用性

仲裁队列应该能够容忍少数队列成员变得不可用,而对可用性没有或几乎没有影响。

请注意,根据使用的分区处理策略,RabbitMQ 可能会在恢复期间重新启动自己并重置节点,但只要这种情况没有发生,此可用性保证应该成立。

例如,拥有三个副本的队列可以容忍一个节点故障而不会丢失可用性。拥有五个副本的队列可以容忍两个,等等。

如果无法恢复仲裁数量的节点(例如,如果 3 个 RabbitMQ 节点中的 2 个永久丢失),则队列将永久不可用,需要强制删除并重新创建。

与领导者断开连接或参与领导者选举的仲裁队列跟随者副本将忽略发送到它的队列操作,直到它们意识到新选出的领导者。日志中将有关于此类事件的警告(received unhandled msg 等)。一旦副本发现新选出的领导者,它将从领导者同步它没有的队列操作日志条目,包括已丢弃的条目。因此,仲裁队列状态将保持一致。

仲裁队列性能特征

仲裁队列旨在以延迟换取吞吐量,并且已经过测试,并在 3、5 和 7 节点配置中以多种消息大小与持久性经典镜像队列进行了比较。

在使用消费者确认和发布者确认的场景中,仲裁队列的吞吐量优于经典镜像队列。例如,请查看使用 3.10 的这些基准测试另一个使用 3.12 的基准测试

由于仲裁队列在执行任何操作之前都会将所有数据持久化到磁盘,因此建议使用尽可能快的磁盘以及某些性能调整设置。

仲裁队列还受益于消费者使用更高的预取值,以确保消费者在确认通过系统流动时不会被饿死,并允许及时地传递消息。

由于仲裁队列的磁盘 I/O 密集特性,随着消息大小的增加,其吞吐量会下降。

仲裁队列的吞吐量还会受到副本数量的影响。仲裁队列拥有的副本越多,其吞吐量一般越低,因为必须做更多的工作来复制数据并达成共识。

可配置设置

可以使用 高级 配置文件调整一些新的配置参数。

请注意,所有与 资源占用 相关的设置都记录在单独的部分。

ra 应用程序(即法定人数队列使用的 Raft 库)具有 一组可调参数

rabbit 应用程序提供了一些与法定人数队列相关的配置项。

advanced.config 配置键描述默认值
rabbit.quorum_cluster_size

设置默认法定人数队列集群大小(可以在声明时被 x-quorum-initial-group-size 队列参数覆盖)。

3
rabbit.quorum_commands_soft_limit

这是一个与流量控制相关的参数,定义了通道在进入流量控制之前接受的最大未确认消息数量。当前默认值被配置为在多个发布者发送到同一个法定人数队列时提供良好的性能和稳定性。如果应用程序通常每个队列只有一个发布者,则可以增加此限制以提供略微更好的入口速率。

32

法定人数队列配置示例

以下 advanced.config 示例修改了上面列出的所有值

[
%% five replicas by default, only makes sense for nine node clusters
{rabbit, [{quorum_cluster_size, 5},
{quorum_commands_soft_limit, 512}]}
].

毒丸消息处理

法定人数队列支持处理 毒丸消息,即导致消费者重复重新排队传递的消息(可能是由于消费者故障),以至于消息永远不会被完全消费和 确认,以便它可以被 RabbitMQ 标记为删除。

法定人数队列跟踪不成功传递尝试的数量,并在包含在任何重新传递消息中的 "x-delivery-count" 标头中公开它。

可以使用 策略 参数 delivery-limit 为队列设置传递限制。

当消息被返回的次数超过限制时,消息将被丢弃或 死信(如果配置了 DLX)。

法定人数队列使用的资源

法定人数队列针对数据安全性和性能进行了优化,并且通常比稳定工作负载下的经典镜像队列需要更多资源(磁盘和 RAM)。每个法定人数队列进程都会维护一个队列中消息的内存索引,每个消息至少需要 32 字节的元数据(如果消息被返回或设置了 TTL,则更多)。因此,法定人数队列进程将至少使用 1MB 来存储队列中的每 30000 条消息(消息大小无关)。您可以根据队列数量和预期或最大消息数量进行粗略估算。保持队列简短是保持低内存使用量的最佳方法。 设置所有队列的最大队列长度 是限制总内存使用量的好方法,如果队列由于任何原因变长。

此外,给定节点上的法定人数队列共享所有操作的预写日志 (WAL)。WAL 操作存储在内存中并写入磁盘。当当前 WAL 文件达到预定义限制时,它将被刷新到磁盘上的 WAL 段文件中,系统将开始释放该批次日志条目使用的内存。然后,随着消费者 确认传递,段文件会随着时间的推移而压缩。压缩是回收磁盘空间的过程。

可以控制 WAL 文件大小限制,在该限制下它将被刷新到磁盘。

# Flush current WAL file to a segment file on disk once it reaches 64 MiB in size
raft.wal_max_size_bytes = 64000000

该值默认为 512 MiB。这意味着在稳定负载期间,WAL 表内存占用可能会达到 512 MiB。您可以预期您的内存使用情况如下

Quorum Queues WAL memory usage pattern

由于内存释放可能需要一些时间,因此我们建议为 RabbitMQ 节点分配至少 3 倍于默认 WAL 文件大小限制的内存。高吞吐量系统需要更多内存。对于这些系统来说,4 倍是一个好的起点。

重复重新排队

在内部,法定人数队列使用日志实现,在该日志中,所有操作(包括消息)都会被持久化。为了避免此日志变得太大,它需要定期截断。为了能够截断日志的一部分,需要确认该部分中的所有消息。持续 拒绝或 Nack 同一条消息并将其 requeue 标志设置为 true 的使用模式可能会导致日志无限制地增长,并最终填满磁盘。

如果未设置 传递限制,则被拒绝或 Nack 回到法定人数队列的消息将被返回到队列的 末尾。这避免了上述场景,其中重复重新排队导致 Raft 日志无限制地增长。如果设置了 传递限制,它将使用将消息返回到队列头部的原始行为。

原子使用增加

法定人数队列的内部实现将队列名称转换为 Erlang 原子。如果不断创建和删除具有任意名称的队列,可能会在原子表大小达到默认限制 500 万时威胁到 RabbitMQ 系统的长期稳定性。

虽然法定人数队列并非设计用于高 churn 环境(非镜像经典队列是这些环境的最佳选择),但如果确实需要,可以增加限制。

请参阅 运行时指南 了解更多信息。

法定人数队列性能调整

本节旨在涵盖一些可调整参数,这些参数可能会提高 某些工作负载 下法定人数队列的吞吐量。其他工作负载可能不会看到任何增加,或者可能会观察到这些设置的吞吐量下降。

将此处的值和建议用作 起点,并进行自己的基准测试(例如,使用 PerfTest)来确定哪种值组合最适合特定工作负载。

调整:Raft 段文件条目计数

具有较小消息和较高消息速率的工作负载可以从以下配置更改中受益,该更改增加了单个预写日志文件中允许的 Raft 日志条目(例如排队的消息)数量

# Positive values up to 65535 are allowed, the default is 4096.
raft.segment_max_entries = 32768

不支持大于 65535 的值。

调整:Linux 预读

此外,前面提到的具有较高的小消息速率的工作负载可以从更高的 预读 中受益,这是一个可配置的 Linux 上存储设备的块设备参数。

要检查有效的 预读 值,请使用 blockdev --getra 并指定托管 RabbitMQ 节点数据目录的块设备

# This is JUST AN EXAMPLE.
# The name of the block device in your environment will be different.
#
# Displays effective readahead value device /dev/sda.
sudo blockdev --getra /dev/sda

要配置 预读,请对托管 RabbitMQ 节点数据目录的块设备使用 blockdev --setra

# This is JUST AN EXAMPLE.
# The name of the block device in your environment will be different.
# Values between 256 and 4096 in steps of 256 are most commonly used.
#
# Sets readahead for device /dev/sda to 4096.
sudo blockdev --setra 4096 /dev/sda