RabbitMQ 流的交付优化

RabbitMQ 流旨在应对高吞吐量场景,但当你的输入速率较低时会发生什么呢?低消息速率会显著影响交付性能,使消息消费速率降低一个数量级。RabbitMQ 4.2 版本引入了一项优化,大幅提升了低吞吐量流下的交付速率,这对所有受支持的协议都有益处。

RabbitMQ 流旨在应对高吞吐量场景,但当你的输入速率较低时会发生什么呢?低消息速率会显著影响交付性能,使消息消费速率降低一个数量级。RabbitMQ 4.2 版本引入了一项优化,大幅提升了低吞吐量流下的交付速率,这对所有受支持的协议都有益处。

通过流进行发布、存储和消费

当发布应用程序使用流协议向RabbitMQ流发送消息时,客户端库会将它们批量打包成发布帧。RabbitMQ接收这些消息并进行聚合,通常将来自多个发布者的消息合并为单个存储单元——即“数据块”。该数据块随后会被持久化存储在文件系统中。

当消费者通过流协议订阅流时,RabbitMQ会通过网络以顺序分块的方式分发消息。每个分块中封装的消息数量(即分块大小)是影响吞吐量的关键因素:包含数百条消息的大分块能显著提升消费者的消息交付速率。

输入速率是决定分块大小的主导因素:输入速率越高,分块越大。流设计之初就以高吞吐量为目标。

小数据块的问题

当输入速率较低时会发生什么?每个数据块将仅包含少量消息——最坏情况下甚至只有一条消息。这将消除批处理的优势:每个帧向消费者传递的消息寥寥无几。尽管数据块分发经过优化,仍需执行多次系统调用(读取文件系统和写入套接字)。

虽难以给出确切数值,但假设某流消费者在分块大小为300时能以每秒数百万条消息的速度读取流数据,当分块大小降至15时,该速率可能跌至每秒约20万条消息。关键在于速率变化比例而非绝对数值。

所幸存在优化小分块处理的方法。

小数据块优化:预读

流通常具有一致的结构:如果某个流在某处包含小块数据,那么它很可能主要由小块组成。因此,当刚读取的块较小时,为何不尝试预读取多个块呢?分发多个块仍需多个帧,但只需从文件系统读取一次,从而节省了昂贵的系统调用。

预读限制为4,096字节,因此并非所有小块流都能受益于此优化。尽管如此,这个相当简单的想法仍能显著提升目标流的传输速率。更值得一提的是,RabbitMQ将此技术应用于所有协议——不仅限于流协议,还包括AMQP协议。

性能结果

我们使用3节点集群和一台虚拟机运行PerfTest与Stream PerfTest测试。所有虚拟机均为m7i.4xlarge AWS实例。我们创建数据流并向其中注入300万条消息,每条数据流的消息大小和分块大小均有所不同。随后通过PerfTest(AMQP 0.9.1协议)和Stream PerfTest(流协议)消费所有消息。测试对象为RabbitMQ 4.1.4及4.2.0-beta.3版本。

我们使用专为此目的编写的工具向流填充消息,以确保获得预期分块大小。以下是用于消费消息的性能工具命令:

# PerfTest
java -jar perf-test.jar --producers 0 --consumers 1 --predeclared --qos 200 \
    --stream-consumer-offset first --cmessages 3000000 --queue stream
    
# Stream PerfTest
java -jar stream-perf-test.jar --producers 0 --consumers 1 --offset first \
    --initial-credits 50 --no-latency --cmessages 3000000 --streams stream

12字节消息,AMQP 0.9.1

image.png

即使12字节的消息体量微小,我们仍能看到预读策略效果显著:对于每块包含1条消息的数据流,其处理速度提升10倍。即便在每块包含384条消息的数据流中,处理速率仍保持两倍优势。

12字节消息,流协议

image.png

采用流协议时,单块消息流的吞吐量提升近10倍(16,000 vs 134,000 消息/秒)。预读机制在每块128条消息之前表现更优。

其他消息尺寸的测试结果保持一致,我们将在文末进行讨论。

48字节消息,AMQP 0.9.1

image.png

48字节消息,流协议

image.png

256字节消息,AMQP 0.9.1

image.png

256字节消息,流协议

image.png

512字节消息,AMQP 0.9.1

image.png

512字节消息,流协议

image.png

1024字节消息,AMQP 0.9.1

image.png

1024字节消息,流协议

image.png

结果分析

流协议的吞吐量提升符合预期:小分块流的吞吐量更高,但当分块达到预读限制(4,096字节)时保持不变。在1,024字节消息中这一现象尤为显著:当分块大小为1和2(低于预读取限制)时速率提升明显,而分块大小为4和6(达到或超过限制导致预读取机制失效)时速率基本持平。

AMQP 0.9.1 的趋势类似,但当数据块超过预读取限制后仍能观察到性能提升。在 AMQP 0.9.1(及其他非流式协议如 AMQP 1.0)中,RabbitMQ 采用迭代器式方法分发消息。该机制原本就包含某种形式的预读取,但如今更具侵略性且能更好地适应数据块大小。这解释了 AMQP 0.9.1 取得的良好结果——其他非流式协议也应能获得类似表现。

最后的细节

预读优化默认处于启用状态,但可通过将 stream.read_ahead 配置项设为 false 全局禁用。

大块数据仍以优化方式分发给消费者:块头部在内存中读取,而块数据(消息)则通过零拷贝方式经由套接字发送。

结论

RabbitMQ 4.2 的预读优化证明,有时未雨绸缪大有裨益——在所有协议下,对小数据块流的性能提升高达10倍。谁能想到,从磁盘读取时稍显贪婪的做法,竟能让消费者如此欣喜?

原文地址:https://www.rabbitmq.com/blog/2025/09/26/stream-delivery-optimization

  

生活总会给你答案的,但不会马上把一切都告诉你。只要你肯等一等,生活的美好,总在你不经意的时候,盛装莅临。
0 不喜欢
说说我的看法 -
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号