RabbitMQ 4.1 性能改进
RabbitMQ 4.1 即将发布(更新:已发布),并且一如既往,除了新功能之外,我们还进行了一些内部改进,以提供更好的性能。
至少有 4 项值得注意的改进
- 仲裁队列的内存使用率更低且更稳定
- 消费长仲裁队列时性能大幅提升
- WebSocket 连接性能更佳
- TCP 连接内存使用率更低和/或吞吐量更高
仲裁队列 (Quorum Queues):降低内存占用
在许多情况下,RabbitMQ 4.1 中的仲裁队列内存占用更低。您可能已经注意到,过去仲裁队列的内存使用模式呈现锯齿状。它们会填满一个用于近期 Raft 操作的内存缓冲区(缓存),一旦填满,缓冲区会被清空,然后再重新填充。
在 RabbitMQ 4.1 中,这些条目的删除频率大大提高,从而在多种条件下实现了更稳定的内存占用。以下是一个集群在运行 4.0 版本并随后升级到 4.1 版本后的内存使用情况:

工作负载的具体细节并非至关重要,因为这种差异在许多不同的工作负载下都是可见的,但为了完整起见,详细情况如下:
- 共有 10 个仲裁队列
- 所有消息大小均为 1kb
- 每个队列从单个发布者处接收每秒 500 条消息(即所有队列总计每秒 5000 条消息)
- 每个队列有一个消费者(绝大多数消息在发布后 10 毫秒内被消费)
- 由于所有消息都能及时被消费,队列实际上处于空闲状态
值得注意的是,在所有条件下都不能期望出现如此低且稳定的内存占用。例如,仲裁队列会在内存中保留队列中消息的元数据,因此如果您在队列中有大量堆积的消息(消息未被立即消费),这些元数据将占用内存。还有其他因素和内存结构会根据工作负载而增长。尽管如此,在许多常见情况下,内存占用应该会更低且波动更小。
仲裁队列:分担磁盘读取负载
让我们考虑一个完全不同的工作负载——消息在队列中堆积,然后消费者需要追赶进度以清空队列。从历史上看,仲裁队列可能会被大量的消费者涌入所压垮,特别是如果消息很大且消费者请求大量消息(无论是拥有巨大的预取缓冲区,还是存在大量消费者,或者两者兼有)。在这种场景下,队列可能忙于从磁盘读取旧消息(以便分发给消费者),导致发布者不得不等待很长时间才能让队列接收其消息。
在 RabbitMQ 4.1 中,此类磁盘读取被分担到了 AMQP 0.9.1 通道或 AMQP 1.0 会话进程中(取决于所使用的协议)。队列的工作压力显著降低,并能够继续为发布者提供服务。
让我们看看 4.0 和 4.1 之间发布和消费速率的差异

此图表反映的情况如下:
- 我们运行了两个集群,4.0 版本(绿线)和 4.1 版本(黄线)
- 两个集群均接收每秒约 6000 条消息,每条消息 20kb
- 初始状态下没有消费者;因此,消费速率为零
- 一段时间后,消费者启动并尝试消费消息
- 在每个环境中,现在有 10 个消费者,每个消费者拥有 300 条消息的预取缓冲区
- 4.0 环境不堪重负——发布速率下降到仅每秒约 100 条消息
- 与此同时,4.1 环境继续为发布者提供服务,没有任何明显影响
- 此外,4.1 环境中的消费速率几乎翻了一番
- 一旦积压的消息被消费完毕,两个环境都能处理每秒约 7000 条消息的吞吐量
不仅发布者没有被限流,消费者也能够以快得多的速度消费消息!
WebSocket 连接性能提升
为了提供 HTTP 连接,RabbitMQ 使用了一个流行的 Erlang HTTP 服务器 Cowboy(由 Loïc Hoguin 在加入 RabbitMQ 团队前很久开发)。RabbitMQ 4.1 将 Cowboy 升级到 2.13.0 版本,该版本显著提升了 WebSocket 性能,受益者包括所有依赖 Cowboy 的系统,当然也包括 RabbitMQ。因此,对于任何使用基于 WebSocket 的 AMQP、MQTT 或 STOMP 的用户来说,升级到 RabbitMQ 4.1 将特别有益。
TCP 缓冲区自动调优
Cowboy 2.13.0 发布博客中描述的一项关键改进是动态 TCP 缓冲区自动调优。对于 WebSocket 连接,这些 Cowboy 的改进会自动让 RabbitMQ 用户受益,因为 Cowboy 负责处理 RabbitMQ 的 HTTP 连接。
在 RabbitMQ 4.1 中,我们将相同的 TCP 缓冲区自动调优机制集成到了 AMQP 监听器中,这是一个完全独立的代码路径,不使用 Cowboy(因为 Cowboy 是 HTTP 服务器)。得益于此项工作,RabbitMQ 在 AMQP 0.9.1 和 1.0 连接上应能使用更少的内存,且不会产生明显的性能损失。节省的内存量取决于您当前的缓冲区大小和连接数量,但在我们的测试中,在一个拥有数千个连接的系统中,它节省了几百 MB 的内存。
值得指出的是,本段中讨论的缓冲区是用户空间缓冲区,不应与作为内核缓冲区的 recbuf / sndbuf 混淆。后者可以进行静态配置,如果不配置,它们会由 Linux 内核自动调优(其他操作系统上的行为可能有所不同)。
tcp_listen_options.buffer 的值(过去用于控制现已自动调优的缓冲区大小)将被忽略。
