Kafka 事件承载状态传输系统是否应该使用 GlobalKTable 来实现本地查询?

Should Kafka event carried state transfer systems be implemented using a GlobalKTable for local queries?

The event carried state transfer removes the need to make remote calls to query information from other services.

让我们假设一个实际案例:

  1. 我们有一个客户服务将 CustomerCreated/CustomerUpdated 事件发布到客户 Kafka 主题。

  2. 送货服务监听订单主题

  3. 当运输服务读取 OrderCreated 事件时,它需要访问客户地址。运输服务将已经在本地提供用户信息,而不是向客户服务发出 REST 调用。它保存在具有持久存储的 KTable/GlobalKTable 中。

我的问题是关于我们应该如何实现这一点:我们希望这个系统具有弹性和可扩展性,因此会有多个客户和运输服务实例,这意味着也会有多个分区用于客户和订单主题。

我们可以找到这样的场景:运输服务读取了一个 OrderCreated(orderId=1, userId=7, ...) 事件,但是如果它使用 KTable 来保存和访问本地用户信息,userId=7 可能不在那里,因为处理该 userId 的分区可能已分配给其他运输服务实例。

这个问题可以使用 GlobalKTable 解决,这样所有运输服务实例都可以访问所有客户。

  1. 这是(GlobalKTable)实现该模式的推荐方法吗?

  2. 当客户数量非常大时,在每个运输服务实例中复制整个客户数据集是否有问题?

  3. this/should这个案例可以用KTable以某种方式实现吗?

你可以用 GKTableKTable 来解决这个问题。之前的数据结构被复制,因此整个 table 在每个节点上都可用(并占用更多存储空间)。后者是分区的,因此数据分布在各个节点上。正如您所说,这有副作用,处理 userId 的分区可能不会处理相应的客户。您可以通过重新分区其中一个流来解决此问题,以便它们共同分区。

因此,在您的示例中,您需要使用运输服务中的客户信息来丰富订单事件。您可以: a) 使用 GlobalKTable 的客户信息并在每个节点上加入该信息 b) 使用客户信息的 KTable 并执行相同的操作,但在进行扩充之前,您必须使用 selectKey() 运算符重新加密以确保数据是共同分区的(即相同的键将在同一个节点)。您还必须在 Customer 和 Orders 主题中拥有相同数量的分区。

Confluent Microservices Examples 中的 Inventory Service Example 做了类似的事情。它重新键入订单流,以便它们按 productId 分区,然后加入 KTable 的库存(也按 productId 键入)。

关于您的个人问题:

  1. GlobalKTable 是实施该模式的推荐方法吗? 两者都有效。如果您的服务因任何原因丢失存储空间,GKTable 有更长的最坏情况重新加载时间。 KTable 会有稍长的延迟,因为数据必须重新分区,这意味着将数据写出到 Kafka 并再次读回。

  2. 当客户量很大时,在每个运输服务实例中复制整个客户数据集是否有问题? 主要区别在于前面提到的最坏情况下的重新加载时间。尽管技术上 GKTableKTable 的语义略有不同(GKTable 在启动时完全加载,KTable 根据事件时间增量加载,但这与这个问题并不严格相关)

  3. this/should这个案例可以用KTable以某种方式实现吗? 见上。

另请参阅:Microservice Examples, Quick start, Blog Post