NATS Jetstream 是不是通过密钥提供消息排序?
Posted
技术标签:
【中文标题】NATS Jetstream 是不是通过密钥提供消息排序?【英文标题】:Does NATS Jetstream provide message ordering by a key?NATS Jetstream 是否通过密钥提供消息排序? 【发布时间】:2021-10-29 06:03:48 【问题描述】:我是 NATS Jetstream 的新手,我一直在阅读他们的官方文档 (https://docs.nats.io/jetstream/jetstream) 以了解其概念并将其与 Kafka 进行比较。我的主要用例之一是解决基于特定 ID 的消息/事件排序问题(例如 Kafka 世界中的 partition key
)。
例如,Order
实体有几个更新事件,我的系统需要以相同的顺序使用特定 Order
的事件。在这种情况下,我会在发布到 Kafka 主题时使用 order-id
作为分区键。如何在 Jetstream 中完成此操作?
我在 Jetstream 中遇到过重复数据删除密钥 (Nats-Msg-Id
),但我认为此功能更类似于 Kafka 中的主题压缩。我说的对吗?
尽管如此,我还是用 Golang 编写了以下代码用于发布:
order = Order
OrderId: orderId,
Status: status,
orderJson, _ := json.Marshal(order)
dedupKey := nats.MsgId(order.OrderId)
_, err := js.Publish(subjectName, orderJson, dedupKey)
我这样做对吗?特定 orderId 的所有订单是否会转到 Jetstream 世界中消费者组内的同一消费者,从而保持顺序?
编辑 1
这是我从@tbeets 的建议中得到的。例如,我预定义了 10 个流主题,例如 ORDER.1
、ORDER.2
、ORDER.3
....ORDER.10
在发布方面,我可以通过order-id%10+1
找到我想要发布的确切流主题。所以在这里,我们已经实现了相同 orderId 的所有更新事件每次都会转到相同的流主题。
现在,在订阅者方面,我有 10 个消费者组(每个消费者组中有 10 个消费者),每个消费者都从特定的流主题中消费,例如 consumerGroup-1
从 ORDER.1
消费,consumerGroup-2
从 @ 消费987654337@等等……
假设order-id
111 出现了 2 个订单更新事件,它们将映射到 ORDER.1
流主题,相应地 consumerGroup-1
将消耗这两个事件。但是在这个consumerGroup中,两个更新事件可以去到不同的消费者,如果其中一个消费者有点忙或有点慢,那么在整体层面上,订单更新事件消费可能不同步或乱序.
Kafka 使用分区键的概念解决了这个问题,因为消费者组的消费者被分配到特定的分区。因此,同一个 orderId 的所有事件都被同一个消费者消费,从而保持订单更新事件消费的顺序。如何在 Jetstream 中解决这个问题?
【问题讨论】:
看起来我的 Edit-1 部分问题没有解决方案。现在可以做的最好的事情是每个消费者组只有一个消费者(对于每个消费者组,例如consumerGroup-1
,consumerGroup-2
....)检查一下:github.com/nats-io/nats-architecture-and-design/pull/36/files Jetstream 计划解决此用途- 未来的案例。
【参考方案1】:
在 NATS 中,您的发布主题可以包含多个分隔标记。因此,例如,您的 Order 事件可以发布到 ORDER.store.orderid,其中最后两个标记特定于每个事件,并提供您的用例所需的任何切片和骰子维度。
然后为 ORDER 定义一个 JetStream。>(即所有事件)。可以在 JetStream 上创建 N 个消费者(临时或持久),每个消费者都有一个可选的过滤器定义,以满足您的用例需求(例如 ORDER.Store24.>)在底层流的消息上。 JetStream 保证消息(过滤或未过滤)按接收顺序传递。
【讨论】:
orderid
是我要消费消息的唯一键,因此您可以删除store
概念。现在,如果我像 ORDER.orderid 这样动态创建多个主题,那么我将需要数千个消费者组(每个订阅一个特定的流主题,如 ORDER.1234、ORDER.1245 等。所以会有一个消费者/每个 Stream 主题的消费者组。这就是你的意思吗?
不现实。如果您有一个想要一起处理的自然主题维度(例如来自特定商店的订单),您可以使用它。如果您不这样做,那么您可以使用散列或其他算法(在您的发布客户端中)创建一个合成主题维度,将您的订单分成 N 个“桶”,您可以在以后独立于流中使用每个“桶”。这类似于 Kafka 通过每个消息键的哈希方案将主题持久化到 N 个静态分区中,但与 Kafka 不同的是,它对于您的应用程序是完全动态的,您可以稍后根据需要更改方案。
是的,正如来自 RI 的链接功能讨论说明,您的订阅应用程序协调其客户端实例,以便每个 JS 消费者只有一个活动实例(即 consumerGroup-1、consumerGroup-2、. ..)。此外,尽管您可以在应用程序代码中进行管理,但将每个 JS Consumer 上的 max ack pending 设置为 1 可以进一步确保每个实例在继续 JS Consumer 定义中的下一个有序消息之前接收和确认。
如何重新平衡这个方案?以上是关于NATS Jetstream 是不是通过密钥提供消息排序?的主要内容,如果未能解决你的问题,请参考以下文章