我正在学习 Kafka,在这里阅读介绍部分
https://kafka.apache.org/documentation.html#introduction
特别是关于消费者的部分。在引言的倒数第二段中写道
卡夫卡做得更好。通过在主题中拥有并行概念(分区),Kafka 能够在消费者进程池上提供排序保证和负载平衡。这是通过将主题中的分区分配给消费者组中的消费者来实现的,以便每个分区仅由组中的一个消费者使用。通过这样做,我们确保消费者是该分区的唯一读取者并按顺序使用数据。由于有许多分区,这仍然可以平衡许多消费者实例的负载。但是请注意,消费者实例不能多于分区。
我的困惑源于最后一句话,因为在该段上方的图片中,作者描述了两个消费者组和一个 4 分区主题,消费者实例比分区多!
消费者实例不能多于分区也是没有意义的,因为分区会非常小,而且为每个消费者实例创建新分区的开销似乎会使 Kafka 陷入困境。我知道分区用于容错和减少任何一台服务器上的负载,但上面的句子在应该能够同时处理数千个消费者的分布式系统的上下文中没有意义。
好的,要理解它,需要理解几个部分。
为了提供订购总订单,消息只能发送给一个消费者。否则效率会非常低,因为它需要等待所有消费者都收到消息,然后再发送下一个消息:
但是,尽管服务器按顺序分发消息,但消息是异步传递给消费者的,因此它们可能会乱序到达不同的消费者。这实际上意味着消息的顺序在并行消费的情况下会丢失。消息传递系统通常通过“独占消费者”的概念来解决这个问题,它只允许一个进程从队列中消费,但这当然意味着处理过程中没有并行性。卡夫卡做得更好。通过在主题中拥有并行概念(分区),Kafka 能够在消费者进程池上提供排序保证和负载平衡。这是通过将主题中的分区分配给消费者组中的消费者来实现的,以便每个分区仅由组中的一个消费者使用。通过这样做,我们确保消费者是该分区的唯一读取者并按顺序使用数据。由于有许多分区,这仍然可以平衡许多消费者实例的负载。但是请注意,消费者实例不能多于分区。 Kafka 仅提供分区内消息的总顺序,而不是主题中不同分区之间的消息。
此外,您认为的性能损失(多个分区)实际上是性能提升,因为 Kafka 可以完全并行执行不同分区的操作,同时等待其他分区完成。
图片显示了不同的消费者组,但每个分区最多只能有一个消费者的限制是在一个组内。您仍然可以拥有多个消费者组。
开头描述了两个场景:
如果所有消费者实例都具有相同的消费者组,那么这就像传统的队列平衡消费者负载一样。如果所有消费者实例都有不同的消费者组,那么这就像发布订阅一样,所有消息都广播给所有消费者。
因此,您拥有的订阅者组越多,性能越低,因为 kafka 需要将消息复制到所有这些组并保证总顺序。
另一方面,您拥有的组越少,分区越多,您从并行化消息处理中获得的收益就越多。
重要的是要记住 Kafka 为每个 [consumer-group、topic、partition] 保留一个偏移量。这就是原因。
我猜句子
但是请注意,消费者实例不能多于分区。
指的是“自动消费者组重新平衡”模式,当您只需订阅()一些消费者到主题列表时的默认消费者模式。
我假设是因为,至少在 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);
并且您将有两个(或更多)消费者从同一个分区读取。
现在,“问题”是两个消费者将共享相同的偏移量,您没有其他选择,因为只有一个组、主题和分区在起作用。
如果两个消费者同时读取当前偏移量,那么它们都将读取相同的值,并且它们都将获得相同的消息。
如果您希望每个消费者读取不同的消息,则必须同步它们,以便一次只能获取并提交偏移量。
Kafka 消费者组模型是队列机制的混合体,其中一个消费者实例读取的消息会立即从队列中删除,而发布/订阅机制则在设置保留期或直到它过期并且可以使用之前不会删除消息。所有消费者实例,直到到期。因此,如果您有一个要使用发布/订阅模型的用例,但又想将其用作排队机制,您可以为所有消费者实例创建消费者组。鉴于 Kafka 在单个消费者组内的消费者实例之间分配分区,可以保证 1 条消息只处理一次。如果 Kafka 允许您在单个消费者组中拥有更多消费者实例,那么它就超出了拥有消费者组的目的。
考虑这个例子:
REST API pub1 向具有 4 个分区 part1 到 part4 的 topic1 发布了 4 条消息,因此每个部分都有 1 条消息。
您有 2 个微服务 sub1 和 sub2 作为订阅者,每个微服务有 4 个实例在运行。
现在,如果您创建 2 个消费者组,每个 miroservice sub1instance1 将映射到 part1,sub1instance2 将映射到 part2,等等类似地 sub2instance1 将映射到 part1,sub2instance2 映射到 part2 等等。
只要您在每个消费者组中的消费者实例小于或等于分区数,您的微服务的每个实例将只处理一次消息。在这种情况下,sub1instance1 和 sub2instance 将处理来自 part1 的 msg1。
如果消费者实例多于分区,则 Kafka 必须将相同的分区分配给多个消费者实例,因此映射到该分区的每个消费者实例将多次处理消息。这就是为什么 Kafka 阻止我们在一个消费者组中拥有比消费者组订阅的主题中的分区数量更多的消费者实例的原因。
希望这是有道理的。
在 Kafka 中,只有一个消费者实例可以消费来自分区的消息。如果消费者实例多于分区,则不会使用额外的消费者实例。所以 kafka 不允许这些额外的消费者实例。
现在,如果多个消费者可以消费分区,那么消息消费就不会有任何顺序。这就是为什么kafka不允许每个分区有多个消费者的原因
Kafka 不能支持每个分区多个消费者是有原因的。
Kafka 代理将数据写入每个分区的文件。所以假设如果配置了两个分区,broker 将创建两个文件并分配多个可以发送消息的消费者组。
现在对于每个分区,只有一个消费者根据文件的偏移量消费消息。例如,消费者 1 将首先从文件偏移量 0 到 4096 读取消息。现在这些偏移量是有效负载的一部分,因此消费者将知道在请求读取下一条消息时要使用哪个偏移量。
如果多个消费者正在从同一个分区读取,则消费者 1 从偏移量 0-4096 的文件中读取,但消费者 2 仍会尝试从偏移量 0 读取,除非它还接收到发送给消费者 1 的消息。现在,如果将相同的消息发送给多个消费者,则这不是负载均衡,因此 Kafka 将它们划分为消费者组,因此所有消费者组都可以接收消息,但在消费者组内,只有一个消费者可以接收消息。
让我们这样想,我们知道一个消费者组可以订阅多个主题,对吧?在这里,我们也可以假设它订阅的每个主题都有不同的编号。分区,有可能吗?
现在,在这种情况下,没有。实例等于没有。分区规则不能应用于所有主题,因为每个主题都假定有不同的编号。分区,对吧?因此,对于同一个消费者组,我们将有 i == p 用于某些主题,i < p 用于某些主题,而 i > p 用于某些主题。
换句话说,理想情况下,您至少希望没有。消费者组中的实例数等于否。主题中的分区,但如果您最终拥有更多实例,那么它不会失败或造成伤害,即对于该主题,额外的实例将简单地保持空闲状态。
例子:
具有 2 个分区的主题 A
具有 3 个分区的主题 B
具有 3 个实例的消费者组 A[1 2] B[1 2 3] [xyz](消费者组)
现在,对于主题“B”,所有 3 个消费者实例都将处于活动状态(每个从 1 个分区读取),但是,对于主题“A”,只有 3 个消费者实例中的任何 2 个将处于活动状态(即其中 1 个将作为主题空闲只有 2 个分区)。
不定期副业成功案例分享