为微服务扩展 Kafka

Geo*_*arz 6 scalability event-driven-design apache-kafka microservices

问题

我想了解我需要如何设计微服务的消息传递以使其仍然具有弹性和可扩展性。

目标

  • 微服务允许弹性数量的实例根据当前负载自动伸缩。这个特性不应该被Kafka限制。
  • 我们需要保证至少一次交付
  • 我们需要保证涉及同一实体的事件的传递顺序

运行示例

  • 简单地说,假设有 2 个微服务 A 和 B;
  • A1、A2为微服务A的实例;微服务B的B1和B2实例
  • A1 和 A2 发布描述 A 实体上的 CRUD 操作的事件,例如实体被创建、更新、删除。

设计一

  • A1 在主题下发布事件,a.entity.created包括id在消息正文中创建的实体的信息。
  • A1 必须指定将有多少个分区 (p) 以允许消费者并行消费。这将允许可扩展性。
  • B1 和 B2a.entity.created作为消费群体订阅该主题b-consumers。这导致 B 的实例之间的负载分布。

问题

  • a) 涉及同一实体的事件可能会被并行处理并且可能会出现乱序,对吗?
  • b) 这将导致 B 的实例并行处理请求。限制因素是生产者如何定义 p(分区数量)。如果有 5 个分区,但我需要 8 个消费者来应对负载,那么它将无法工作,因为 3 个消费者不会收到事件。我理解正确吗?这将 IMO 无法用于可能想要进一步扩展的弹性微服务。

设计二

  • A1 在a.entity.created.{entityId}导致许多不同主题的主题下发布事件。
  • 每个分区大小设置为 1。
  • B1 和 B2a.entity.created.*使用通配符作为消费者组订阅主题b-consumers。这导致 B 的实例之间的负载分布。

问题

  • c) 同一实体的事件应该保证按顺序传递,因为只有一个分区,对吗?
  • d) 这里是如何处理可扩展性的?分区的数量不应该限制消费者的数量,或者是吗?
  • e) 是否有更好的方法来保证上述目标?

设计 III(感谢 StuartLC)

  • A1根据entityId在topica.entity.createdpartition key下发布事件。
  • 每个分区大小设置为 10。
  • B1 和 B2a.entity.created作为消费群体订阅该主题b-consumers。这导致 B 实例之间的负载分布。由于分区键,关于同一实体的事件将按顺序传递。

问题

  • f) 当 p=10 时,我最多可以有 10 个消费者。这意味着如果使用环境变量,我必须在设计时/部署时估计消费者的数量。我可以将它移动到运行时以便动态调整吗?

Stu*_*tLC 4

您不需要每个实体有一个单独的主题来确保消息的传递顺序。

相反,根据实体 ID(或实体的其他不可变标识符)为每条消息分配分区键,以确保涉及同一实体的事件始终传递到每个主题的同一分区。

即使多个生产者实例为同一实体发布消息,通常也会保留分区上消息的时间顺序(例如,如果 A1 和 A2 都为实体 E1 发布消息,则 E1 的分区键将确保所有 E1 消息均已传递)到同一分区,P1。)。在某些边缘情况下,排序不会被保留(例如,生产者的连接丢失),在这种情况下,您可能会考虑启用幂等性

你是对的,在任何时候,消费者组中最多一个消费者将订阅单个主题分区,尽管一个消费者可以被分配到多个分区。例如,消费者 B2 可能会处理来自分区 P1(按顺序)以及来自另一个分区的所有消息。如果一个消费者死亡,那么另一个消费者将被分配到该分区(转换可能需要几秒钟)。

当消息上提供分区键时,默认情况下,消息到分区的分配是基于分区键的哈希完成的。在极少数情况下(例如,实体分区键非常少),这可能会导致分区上的消息分布不均匀,在这种情况下,如果您发现吞吐量受到不平衡的影响,您可以提供自己的分区策略。

综上所述,在为您的场景配置适当数量的分区后,您应该能够使用一致的分区键来满足您的设计目标,而无需对 Kafka 进行太多定制。