在Apache Kafka中,为什么不能有比分区更多的消费者实例?

alm*_*mel 57 distributed apache-kafka

我正在学习卡夫卡,在这里阅读介绍部分

https://kafka.apache.org/documentation.html#introduction

特别是有关消费者的部分.在引言的倒数第二段中,它读到了

卡夫卡做得更好.通过在主题中具有并行性概念 - 分区 - ,Kafka能够在消费者流程池中提供订购保证和负载平衡.这是通过将主题中的分区分配给使用者组中的使用者来实现的,以便每个分区仅由该组中的一个使用者使用.通过这样做,我们确保使用者是该分区的唯一读者并按顺序使用数据.由于有许多分区,这仍然可以平衡许多消费者实例的负载.但请注意,除分区之外不能有更多的消费者实例.

我的困惑源于最后一句话,因为在该段落的正上方,作者描绘了两个消费者群体和一个4分区主题,消费者实例多于分区!

没有比分区更多的消费者实例也没有意义,因为那时分区将非常小,并且似乎为每个消费者实例创建新分区的开销会使Kafka陷入困境.我知道分区用于容错并减少任何一台服务器上的负载,但上述句子在分布式系统的环境中没有意义,该分布式系统应该能够一次处理数千个消费者.

pet*_*ter 58

好的,要理解它,需要了解几个部分.

  1. 为了提供订购总订单,消息只能发送给一个消费者.否则它将是非常低效的,因为它需要等待所有消费者在发送下一个消息之前接收消息:

但是,尽管服务器按顺序分发消息,但消息是异步传递给消费者的,因此它们可能会在不同的消费者上无序传送.这实际上意味着在并行消费的情况下消息的排序会丢失.消息传递系统通常通过具有"独占消费者"的概念来解决这个问题,该概念只允许一个进程从队列中消耗,但这当然意味着处理中没有并行性.

卡夫卡做得更好.通过在主题中具有并行性概念 - 分区 - ,Kafka能够在消费者流程池中提供订购保证和负载平衡.这是通过将主题中的分区分配给使用者组中的使用者来实现的,以便每个分区仅由该组中的一个使用者使用.通过这样做,我们确保使用者是该分区的唯一读者并按顺序使用数据.由于有许多分区,这仍然可以平衡许多消费者实例的负载.但请注意,除分区之外不能有更多的消费者实例.

Kafka仅对分区内的消息提供总订单,而不是在主题中的不同分区之间.

您认为性能损失(多个分区)实际上也是性能提升,因为Kafka可以完全并行执行不同分区的操作,同时等待其他分区完成.

  1. 该图显示了不同的消费者群体,但每个分区最多一个消费者的限制仅在一个群组内.您仍然可以拥有多个消费者群组.

最初描述了两种情况:

如果所有使用者实例具有相同的使用者组,那么这就像传统的队列平衡对消费者的负载一样.

如果所有消费者实例具有不同的消费者组,则其工作方式类似于发布 - 订阅,并且所有消息都广播给所有消费者.

因此,您拥有的订阅者组越多,性能就越低,因为kafka需要将消息复制到所有这些组并保证总​​订单.

另一方面,较少的组,您拥有的分区越多,您从平行消息处理中获得的越多.

  • 因此,可以通过以下说明来回答这个问题:在单个使用者组中没有比分区更多的消费者实例. (23认同)
  • @peter 很好的答案,但是有一件重要的事情没有得到解决。如果我们想要每个消费者只有一个分区怎么办?这意味着一个组内的分区和消费者数量相同(如果 Kafka 进行了适当的平衡,而且确实如此)。好的,现在我们还想确保即使某些消费者实例发生故障,我们仍然为每个消费者实例拥有一个分区。这样做的合理方法是将更多消费者添加到该组中;虽然一切正常,他们不会做任何事情,但是当某些消费者失败时,他们中的一个会收到该分区。为什么这是不允许的? (2认同)

小智 11

Kafka消费者组模型是队列机制和发布/订阅机制的混合体,其中消息一旦被一个消费者实例读取,就会立即从队列中删除,而发布/订阅机制则直到设置的保留期或过期并且可供使用时才删除消息。所有消费者实例直到过期。因此,如果您有想要使用发布/订阅模型但希望将其用作排队机制的用例,则可以为所有消费者实例创建消费者组。鉴于 Kafka 在单个消费者组内的消费者实例之间分配分区,可以保证 1 条消息仅处理一次。如果 Kafka 允许您在单个消费者组中拥有更多消费者实例,那么它就违背了拥有消费者组的目的。

考虑这个例子:

REST API pub1 向 topic1 发布了 4 条消息,topic1 有 4 个分区(part1 到 part4),因此每个部分都有 1 条消息。

您有 2 个微服务 sub1 和 sub2 作为订阅者,每个微服务有 4 个实例正在运行。

现在,如果您创建 2 个消费者组,则每个 miroservice 都有一个 sub1instance1 将映射到part1,sub1instance2 将映射到part2,以此类推。类似地,sub2instance1 将映射到part1,sub2instance2 映射到part2 等。

只要每个消费者组中的消费者实例小于或等于分区数量,微服务的每个实例就只会处理消息一次。在这种情况下,sub1instance1和sub2instance将处理来自part1的msg1。

如果消费者实例多于分区,那么 Kafka 将必须将相同的分区分配给多个消费者实例,因此映射到该分区的每个消费者实例将多次处理消息。这就是为什么 Kafka 阻止我们在一个消费者组中拥有比消费者组订阅的主题内的分区数量更多的消费者实例。

希望这是有道理的。


Abh*_*nav 7

在 Kafka 中,只有一个消费者实例可以消费来自分区的消息。如果消费者实例多于分区,则不会使用额外的消费者实例。所以kafka不允许这些额外的消费者实例。

现在,如果多个消费者可以消费分区,那么消息的消费将不会有任何顺序。这就是kafka不允许每个分区有多个消费者的原因


Luc*_*lie 6

重要的是要记住,Kafka每个[消费者群体,主题,分区]保留一个偏移量.这就是原因.

我猜这句话

但请注意,除分区之外不能有更多的消费者实例.

指的是"自动消费者群体重新平衡"模式,当您只是订阅()一些消费者的主题列表时,默认的消费者模式.

我假设,因为,至少对于Kafka 0.9.x,没有什么可以防止有几个消费者实例,同一组的成员,从同一个分区读取.

你可以在两个或多个不同的线程中做这样的事情

Properties props = new Properties();
props.put(ConsumerConfig.GROUP_ID_CONFIG, "MyConsumerGroup");
props.put("enable.auto.commit", "false");
consumer = new KafkaConsumer<>(props);
TopicPartition partition0 = new TopicPartition("mytopic", 0);
consumer.assign(Arrays.asList(partition0));
ConsumerRecords<Integer, String> records = consumer.poll(1000);
Run Code Online (Sandbox Code Playgroud)

并且您将有两个(或更多)消费者从同一分区读取.

现在,"问题"是两个消费者将共享相同的偏移,你没有其他选择,因为只有一个组,主题和分区发挥作用.

如果两个消费者同时读取当前偏移量,那么它们将读取相同的值,并且它们都将获得相同的消息.

如果您希望每个使用者阅读不同的消息,您将必须同步它们,因此只有一个消费者可以在时间获取并提交偏移量.


sac*_*tiw 6

我们这样想,我们知道一个消费者组可以订阅多个主题,对吗?这里,我们还可以假设它订阅的每个主题都有不同的编号。分区,有可能吗?

现在,在这种情况下,没有。实例数等于数量 分区规则不能应用于所有主题,因为每个主题假设有不同的编号。分区,对吧?因此,对于相同的消费者组,对于某些主题,我们将有 i == p,对于某些主题,有 i < p,对于某些主题,有 i > p。

换句话说,理想情况下您至少希望没有。消费者组中的实例数量等于数量。主题中的分区,但如果您最终拥有更多实例,那么它不会失败或造成损害,即对于该主题,额外的实例将保持空闲状态。

例子:

现在,对于主题“B”,所有 3 个消费者实例都将处于活动状态(每个从 1 个分区读取),但是,对于主题“A”,只有 3 个消费者实例中的任意 2 个将处于活动状态(即,其中 1 个将作为主题闲置)只有 2 个分区)。