告别同步:RabbitMQ 3.3 中的性能改进
昨天我们已经公布了坏消息,所以今天我们来聊聊(一些)好消息:某些类型的发布和消费现在速度快了很多,尤其是在集群环境中。
RabbitMQ 的各个内部组件通过在它们之间传递消息来进行通信(包括节点内部和集群之间);Erlang 应用程序就是这样工作的。RabbitMQ 的一个设计目标一直是,AMQP 中异步的操作(即发送和接收消息及确认)在服务器内部也应该是异步的。这样做的原因很简单:当你执行同步操作时,你会被等待回复的延迟所限制,因此异步是实现更高消息传递速度的途径。
不幸的是,虽然这一直是一个目标,但我们并不总是能实现。特别是,有两个环节,AMQP 中的异步消息在服务器内部变成了同步:mandatory 发布和使用通过 basic.qos 设置的 prefetch 限制进行消费。这两个问题在 3.3.0 版本中得到了修复。
为了回顾一下,mandatory 发布意味着告知发布者其消息是否未路由到任何队列,而使用 prefetch 限制进行消费则意味着确保你最多只向消费者发送一定数量的未确认消息。
所以,让我们来看一些数据……
Mandatory 发布
| 3.2.4 | 3.3.0 | |
|---|---|---|
| Mandatory 发布 | 5.0kHz 平衡 | 12.9kHz 平衡 |
这个测试涉及在一台机器上的双节点集群,一个发布者连接到一个节点,一个消费者连接到另一个节点,队列位于与消费者相同的节点上。消息很小且非持久化,没有使用 ack 或 confirm。机器是一台 Dell Precision 工作站,但重点在于观察相对性能的变化。
希望你能看到同步在这里对性能的严重影响。请记住,同步消息传递带来的性能损失与网络延迟成正比——这两个节点位于同一台机器上,所以在真实集群中,性能下降会更严重。
另外请注意,在这两种情况下,发送和接收速率是相同的;消息没有在队列中堆积。
使用 prefetch 限制进行消费
我们预期,较高的 prefetch 限制会提供接近无 prefetch 限制的性能,并且随着限制的降低,性能会下降,因为在某些点上,队列必须等到消费者确认一条消息后才能发送下一条。
| 3.2.4 | 3.3.0 | |
|---|---|---|
| 无限制 | 15.0kHz 发送 / 11.0kHz 接收 | 15.8kHz 平衡 |
| prefetch_limit=1000 | 6.2kHz 发送 / 3.6kHz 接收 | 15.8kHz 平衡 |
| prefetch_limit=100 | 6.2kHz 发送 / 3.6kHz 接收 | 13.5kHz 平衡 |
| prefetch_limit=10 | 6.2kHz 发送 / 3.6kHz 接收 | 14.0kHz 发送 / 7.0kHz 接收 |
| prefetch_limit=1 | 18.0kHz 发送 / 0.9kHz 接收 | 18.0kHz 发送 / 0.9kHz 接收 |
此测试与上述测试具有相同的特征,但队列与发布者位于同一节点,并且在消费时使用了确认。
此表中的数字显示了几个有趣的现象。
- 即使在 prefetch 限制关闭的情况下,3.3.0 的性能也略有提升,并防止了消息堆积。这是由于一项新功能,我将在未来的博文中讨论。
- 在 3.3.0 版本中,足够高的 prefetch 限制(使得队列无需等待消费者)不会带来任何性能损耗,而在 3.2.4 版本中,任何 prefetch 限制都会损害性能。
- 在 3.2.4 版本中,10、100 和 1000 之间的所有 prefetch 限制都具有完全相同的(糟糕的)性能——这是因为限制因素实际上是消费通道和队列之间的同步通信。
- 最后,当我们达到 1 的 prefetch 限制时,3.2.4 和 3.3.0 的性能同样糟糕——这是因为限制因素现在变成了我们等待消费者一次发送一个消息的确认所需的时间。
因此,通过这些更改,RabbitMQ 的消息传递内部在所有情况下都是异步的,带来了显著的性能优势。值得指出的是,为了实现这一点,basic.qos 的语义必须略有改变,但这对于如此大的改进来说似乎是微不足道的代价。