跳至主内容

RabbitMQ 3.11 功能预览:Super Streams

·阅读 7 分钟

RabbitMQ 3.11 将带来一个在其历史上名称最酷的功能之一:Super Streams。Super Streams 是一种通过将大型流分区为较小流来扩展的方法。它们与Single Active Consumer集成,以在分区内保留消息顺序。

这篇博文概述了 Super Streams 及其带来的用例。继续阅读以了解更多信息,我们重视您的反馈,以使此功能尽善尽美。

概述

超级流是一种由独立的常规流 (streams) 组成的逻辑流。它是 RabbitMQ Streams 中一种实现扩展发布和消费的方法:将一个大型逻辑流划分为多个分区流,从而将存储和流量分散到多个集群节点上。

超级流仍然是一个逻辑实体:得益于客户端库的智能化,应用程序将其视为一个“大”流。超级流的拓扑结构基于 AMQP 0.9.1 模型,即交换机、队列以及它们之间的绑定关系。

下图显示了一个由 3 个分区组成的 invoices(发票)超级流。它由一个 invoices 交换机定义,并绑定了 3 个流。

A super stream is a structure that sits above streams, allowing to logically group a set of streams. AMQP 0.9.1 resources define its physical topology.
超级流是一种位于流之上的结构,允许对一组流进行逻辑分组。AMQP 0.9.1 资源定义了其物理拓扑。

消息不会经过交换机,而是直接发送到分区流。但是,客户端库会利用拓扑信息来确定将消息路由到何处以及从何处消费。

我们来谈谈这个显而易见的问题:它与 Kafka 相比如何?我们可以将超级流比作 Kafka 主题,将流比作 Kafka 主题的分区。不过,RabbitMQ 流是一个一流的、具有独立名称的对象,而 Kafka 分区是 Kafka 主题的从属部分。这种解释忽略了很多细节,也没有真正的 1 对 1 映射,但对于本文的观点来说已经足够准确了。

超级流还利用了单一活跃消费者 (single active consumer),以在消费者处理期间保持分区内消息的顺序。详情见下文。

请不要误会,超级流并没有废弃单个流,也不会使它们变得无用。它们也不是流的 2.0 版本,因为我们并没有在一开始就把流做错。超级流位于流之上,它们带来了自己的一套特性和功能。你可以继续在适用的场景中使用单个流,也可以探索超级流以应对要求更高的使用场景。

发布到超级流

应用程序发布到超级流的消息必须进入其中一个分区。应用程序可以在客户端库的帮助下选择分区,代理(broker)不处理路由。这种方式非常灵活,避免了服务器端的瓶颈。

应用程序必须至少提供一条信息:从消息中提取的路由键。以下代码片段展示了应用程序如何使用 Java 流客户端库提供此代码:

Producer producer = environment.producerBuilder()
.superStream("invoices") // set the super stream name
.routing(message -> message.getProperties().getMessageIdAsString()) // extract routing key
.producerBuilder()
.build();

producer.send(...);

发布方式与常规流保持一致,只是生产者配置略有不同。发布到流或超级流对应用程序代码的影响不大。

消息会去哪里?在这种情况下,库通过使用 MurmurHash3 对路由键进行哈希处理来选择流分区。该哈希函数提供了良好的均匀性、性能和可移植性,使其成为一个不错的默认选择。在我们的例子中,路由键是发票 ID,因此发票应均匀分布在各个分区中。如果路由键是客户 ID,我们就能保证给定客户的所有发票都位于同一个分区。以下是一个可以利用此点的用例:利用适当的基于时间戳的偏移量规范,按客户进行特定时间段的报告。

谈到处理,让我们看看如何从超级流中消费。

从超级流消费

超级流的分区是常规流,因此应用程序可以像往常一样从中消费。但这意味着需要了解超级流的拓扑结构,而我们将其作为逻辑实体推销,即应用程序视为单个流的东西。幸运的是,在代理的帮助下,客户端库可以做到这一点,使这一切对应用程序透明。

客户端库可以实现一种广为人知的设计模式,并提供一个组合消费者,同时从超级流的所有分区进行消费。

A client library provides a composite consumer that consumes from all partitions at the same time. Applications then see the super stream as an individual stream. This is not enough though.
客户端库提供了一个组合消费者,它同时从所有分区进行消费。应用程序随后将超级流视为一个单独的流。但这还不够。

以这种方式实现的组合消费者存在局限性:如果你为了冗余和可扩展性启动了同一个应用程序的多个实例,它们将消费相同的消息,从而导致重复处理。所有这些都需要协调,幸运的是,我们可以将单一活跃消费者的语义应用于我们的超级流组合消费者。

现在,启用了单一活跃消费者后,我们的组合消费者实例会与代理进行协调,以确保在给定的分区上同一时间只有一个消费者。

Combining super stream consumers and single active consumer. There is only one active consumer on a partition at a time for a given group.
结合超级流消费者和单一活跃消费者。对于给定的组,每个分区在同一时间只有一个活跃消费者。

好消息是,所有这些都是实现细节。应用程序实例可以随时启动和关闭,消费者将根据单一活跃消费者的语义进行激活或停用。

这如何转化为代码?以下是使用 Java 流客户端的示例:

Consumer consumer = environment.consumerBuilder()
.superStream("invoices") // set the super stream name
.name("application-1") // set the consumer name (mandatory)
.singleActiveConsumer() // enable single active consumer
.messageHandler((context, message) -> {
// message processing
})
.build();

这与常规流的消费者类似,只有配置发生了变化,而且重要的是,消息处理代码保持不变。

总结

我们在本篇博客文章中介绍了超级流,这是即将发布的 RabbitMQ 3.11 版本中的一项新功能。超级流是分区的流,它们为 RabbitMQ Stream 带来了可扩展性。结合单一活跃消费者,它们保证了分区内消息按发布顺序进行处理。

这篇博客文章附带一个配套示例项目,提供了逐步演示来说明所涵盖的功能。欢迎查看!

我们很高兴能与 RabbitMQ 社区分享这一新功能,并且期待在 RabbitMQ 3.11 今年晚些时候正式发布 (GA) 之前听到一些反馈

© . This site is unofficial and not affiliated with VMware.