RabbitMQ 4.1 性能改进
RabbitMQ 4.1 即将发布(更新:已发布),并且一如既往,除了新功能之外,我们还进行了一些内部改进,以提供更好的性能。
至少有 4 项值得注意的改进
- 仲裁队列的内存使用率更低且更稳定
- 消费长仲裁队列时性能大幅提升
- WebSocket 连接性能更佳
- TCP 连接内存使用率更低和/或吞吐量更高
仲裁队列:降低内存使用量
在许多情况下,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)都适用。因此,升级到 RabbitMQ 4.1 对任何使用通过 WebSocket 传输的 AMQP、MQTT 或 STOMP 的用户特别有利。
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 连接占用更少的内存,而没有明显的性能损失。节省的内存量取决于您当前的缓冲区大小和连接数,但在我们的测试中,在一个拥有数千个连接的系统中节省了几百兆字节的内存。
值得指出的是,本段讨论的缓冲区是用户空间缓冲区,不应与 recbuf / sndbuf 缓冲区混淆,后者是内核缓冲区。这些可以静态配置,如果未配置,则由 Linux 内核自动调整(其他操作系统上的行为可能不同)。
tcp_listen_options.buffer 的值(以前控制现在自动调整的缓冲区大小)将被忽略。
