RabbitMQ还是Kafka:吞吐接近时,消息语义才决定选择

|作者: QUASA 编辑团队|2 分钟阅读
RabbitMQ还是Kafka:吞吐接近时,消息语义才决定选择

选择 RabbitMQ 还是 Kafka,先看消息完成处理后应当发生什么:任务需要逐条确认、失败后单独重试或改道,事件则可能需要保留,供不同消费者按各自的位置再次读取。RabbitMQ 的官方架构对照认为,RabbitMQ Streams 与 Kafka 的流式吞吐和延迟处于同一量级;它也指出,两者的消息处理保证都不能自动覆盖外部数据库写入等副作用,故障重投时仍须防止重复执行。

功能边界已经交叠。Kafka 的升级说明确认,4.2 版的 share groups 已可用于生产:多个消费者能够协作处理同一分区中的记录,并逐条确认、统计投递次数。因此,现有 Kafka 集群也能承担一些任务队列工作;真正影响选择的,是失败任务能否独立处置、已完成记录如何保留,以及路由规则由谁执行。

任务队列:确认后,工作项如何退出流程

一条消息若代表一项必须完成的工作,RabbitMQ 仲裁队列通常更贴近这个模型。消费者确认后,该工作项退出待处理集合;处理失败时,可以让它重新投递、延后重试或进入死信路径。发布端确认与消费端确认回答的是两个问题:代理是否接收了消息,以及执行者是否完成了工作。前者不能代替后者,消费者也不应在外部操作尚未完成时把任务标成已处理。

RabbitMQ 的仲裁队列与 Streams 采用不同的存储语义。仲裁队列复制工作项及其投递状态,并在向发布者确认前将写入刷到磁盘;Streams 保留可反复读取的日志,消费不会逐条删除记录。即使应用都使用 RabbitMQ,也要先选定队列类型,再讨论持久性、积压和清理成本,不能把仲裁队列的确认条件套到 Streams 上。

Kafka share groups 跟踪单条记录的投递状态,但任务确认后,记录不会立刻从底层 topic 消失;存储占用仍由保留策略决定。若任务要按失败原因改道、安排独立的延迟重试或设置优先级,RabbitMQ 能把更多处理放在代理端;使用 Kafka 时,应用通常要自行组织这些流程。对于只需让一组执行者分担独立工作的场景,share groups 的能力可能已经足够,尤其是在事件原本就写入 Kafka 的系统中。

审计日志与事件重放:要保留哪一种历史

审计和重放首先要求明确保留目标。RabbitMQ Streams 与 Kafka topic 都允许消费者按位置读取仍在保留窗口内的事件,多个读者也可以维持不同的进度;普通工作队列在任务确认后则不承担同样的历史重读职责。要求保存每次变更的审计系统,需要按完整历史规划保留期和容量。“可重放”只意味着记录尚未被清理,并不意味着它会永久存在。

Kafka 的设计文档说明,消费者可以回退偏移量重新读取记录,而日志压缩会为每个键保留最新状态。两种用途不能混用:压缩日志适合重建某个键现在的值,却不能替代必须逐条保存变更的审计记录。对于需要从历史事件重新计算结果的应用,还要保证重放起点仍处于保留窗口内,并确认处理程序能够应对同一事件再次到达。

读者之间是否需要隔离进度,也会改变方案。一批事件可能同时供实时服务、离线计算和故障修复使用;各自管理读取位置,比让执行者争抢同一项工作更符合这类需求。若主要目标只是分发短期任务,持续保存已完成的记录则会增加存储和清理工作。这里的分界是数据的生命周期,而非产品是否提供消费接口。

路由与并行:规则放在代理还是应用

RabbitMQ 的 exchange 和 binding 可依据消息属性,把消息送往不同队列或流。按事件类别、租户或失败原因调整接收方时,路由规则可以由代理维护;Kafka 的生产者通常选择 topic 和分区,消费者订阅 topic,更细的筛选常落在消费应用或额外的处理流程中。因此,路由频繁变化且由多个团队维护时,代理端规则可能减少生产者之间的协调成本。

Streams 有一个容易忽略的例外:使用 RabbitMQ Stream 协议的生产者可以直接写入目标流,不经过 exchange;使用其他受支持协议的生产者则可以经由绑定进入流。比较“RabbitMQ 自带路由”与“Kafka 由生产者选择分区”时,应把实际写入路径说清楚,否则会把工作队列的能力误算到每一种流式配置中。

并行处理也取决于消费模式。Kafka 的常规 consumer group 将分区分配给组内消费者,以分区为单位维持有序读取;share group 允许多个成员协作处理同一分区中的记录,适合逐条工作的分配,但处理完成顺序可能随执行时间变化。RabbitMQ 工作队列把独立任务交给不同执行者,Streams 则更接近按位置读取的事件流。若同一实体的事件必须依次生效,需要先定义排序键以及分区或队列的边界,增加消费者数量本身无法保住跨边界的顺序。

吞吐跑分为什么容易误导

每秒消息数只有连同测试条件才有意义。发布者是逐条等待确认,还是允许多条写入同时在途?生产端是否凑满批次,消费者是否与生产者同时运行?RabbitMQ 测的是仲裁队列还是 Stream,Kafka 的批大小和 acks 如何设置?消息大小、压缩、复制、磁盘以及消费者落后程度都可能改变瓶颈。只改动其中一项,吞吐差异就可能主要来自配置。

一项 2017 年的对比研究在同一台机器上使用 RabbitMQ 3.5.3 与 Kafka 0.10.0.1,测量延迟、吞吐、CPU 和内存;测试中内存占用最高分别未超过 13.4% 和 29.5%,更强的投递保证也改变了延迟表现。这些百分比是该环境中的资源观测值,不是两款产品的固定内存需求,更不能作为当前 RabbitMQ Streams 与 Kafka 的速度排名。

该研究的复制实验同样在单机上运行多个代理实例,因而不能直接代表跨机器复制时的网络开销与故障恢复。测试时 Kafka 生产端使用当时的默认设置;批次是否填满会影响发送效率。把旧版工作队列与新版流日志混为一组,再将结果归因于产品名称,会遮蔽真正的变量:存储类型、确认模式、批处理和复制条件。

比较任务队列时,端到端时间应涵盖发布确认、排队、消费确认以及失败后的重试;比较事件流时,则要观察持续写入、多个独立读者和积压重读并存的情况。峰值吞吐无法说明任务完成耗时,也无法显示消费者落后后从磁盘读取历史记录的代价。只有负载与可靠性要求相近,延迟和吞吐数字才适合并列。

存储与运维如何完成取舍

已完成任务和保留事件会形成不同的存储曲线。RabbitMQ 工作队列确认任务后会逐步回收相应空间,Streams 与 Kafka topic 则按保留策略管理日志。审计窗口越长、独立读者越多,容量规划就越重要;任务积压若只是暂时现象,按长期日志配置保留已完成工作可能没有收益。

需要把较长历史移出本地磁盘时,Kafka 可以配置分层存储,但分层存储文档明确说明,Apache Kafka 没有附带可直接使用的 RemoteStorageManager 实现,启用远端存储还需提供并配置相应实现。这项能力因此伴随额外的集成与运维工作;RabbitMQ Streams 的保留规划则主要受本地磁盘约束。

三类负载可以据此作出不同选择。任务队列若重视逐条完成状态、独立重试和失败改道,优先评估 RabbitMQ 仲裁队列;流程简单且已有 Kafka 集群时,可评估 share groups。审计日志若必须保存每次变更,应选择完整历史保留,并避免用只保留最新状态的压缩日志代替。事件重放可以使用 RabbitMQ Streams 或 Kafka topic:保留窗口、读者进度和运维能力决定哪一种更合适;需要按键重建状态或配置更长历史时,Kafka 的日志机制提供相应选择。

相关阅读:

分享:

订阅我们的新闻通讯

将最新 Web3、AI 和加密货币新闻直接发送到您的邮箱。

0