按照Kafka默认的消费逻辑设定,一个分区只能被同一个消费组(ConsumerGroup)内的一个消费者消费。假设目前某消费组内只有一个消费者C0,订阅了一个topic,这个topic包含7个分区,也就是说这个消费者C0订阅了7个分区,参考下图(1)。
此时消费组内又加入了一个新的消费者C1,按照既定的逻辑需要将原来消费者C0的部分分区分配给消费者C1消费,情形上图(2),消费者C0和C1各自负责消费所分配到的分区,相互之间并无实质性的干扰。
接着消费组内又加入了一个新的消费者C2,如此消费者C0、C1和C2按照上图(3)中的方式各自负责消费所分配到的分区。
如果消费者过多,出现了消费者的数量大于分区的数量的情况,就会有消费者分配不到任何分区。参考下图,一共有8个消费者,7个分区,那么最后的消费者C7由于分配不到任何分区进而就无法消费任何消息。
如果消费者过多,出现了消费者的数量大于分区的数量的情况,就会有消费者分配不到任何分区。参考下图,一共有8个消费者,7个分区,那么最后的消费者C7由于分配不到任何分区进而就无法消费任何消息。
上面各个示例中的整套逻辑是按照Kafka中默认的分区分配策略来实施的。Kafka提供了消费者客户端参数partition.assignment.strategy用来设置消费者与订阅主题之间的分区分配策略。默认情况下,此参数的值为:org.apache.kafka.clients.consumer.RangeAssignor,即采用RangeAssignor分配策略。除此之外,Kafka中还提供了另外两种分配策略: RoundRobinAssignor和StickyAssignor。消费者客户端参数partition.asssignment.strategy可以配置多个分配策略,彼此之间以逗号分隔。
RangeAssignor分配策略
RangeAssignor策略的原理是按照消费者总数和分区总数进行整除运算来获得一个跨度,然后将分区按照跨度进行平均分配,以保证分区尽可能均匀地分配给所有的消费者。对于每一个topic,RangeAssignor策略会将消费组内所有订阅这个topic的消费者按照名称的字典序排序,然后为每个消费者划分固定的分区范围,如果不够平均分配,那么字典序靠前的消费者会被多分配一个分区。
假设n=分区数/消费者数量,m=分区数%消费者数量,那么前m个消费者每个分配n+1个分区,后面的(消费者数量m)个消费者每个分配n个分区。
为了更加通俗的讲解RangeAssignor策略,我们不妨再举一些示例。假设消费组内有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有4个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t0p3、t1p0、t1p1、t1p2、t1p3。最终的分配结果为:
这样分配的很均匀,那么此种分配策略能够一直保持这种良好的特性呢?我们再来看下另外一种情况。假设上面例子中2个主题都只有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
可以明显的看到这样的分配并不均匀,如果将类似的情形扩大,有可能会出现部分消费者过载的情况。对此我们再来看下另一种RoundRobinAssignor策略的分配效果如何。
RoundRobinAssignor分配策略
RoundRobinAssignor策略的原理是将消费组内所有消费者以及消费者所订阅的所有topic的partition按照字典序排序,然后通过轮询方式逐个将分区以此分配给每个消费者。RoundRobinAssignor策略对应的partition.assignment.strategy参数值为:org.apache.kafka.clients.consumer.RoundRobinAssignor。
如果同一个消费组内所有的消费者的订阅信息都是相同的,那么RoundRobinAssignor策略的分区分配会是均匀的。举例,假设消费组中有2个消费者C0和C1,都订阅了主题t0和t1,并且每个主题都有3个分区,那么所订阅的所有分区可以标识为:t0p0、t0p1、t0p2、t1p0、t1p1、t1p2。最终的分配结果为:
如果同一个消费组内的消费者所订阅的信息是不相同的,那么在执行分区分配的时候就不是完全的轮询分配,有可能会导致分区分配的不均匀。如果某个消费者没有订阅消费组内的某个topic,那么在分配分区的时候此消费者将分配不到这个topic的任何分区。
举例,假设消费组内有3个消费者C0、C1和C2,它们共订阅了3个主题:t0、t1、t2,这3个主题分别有1、2、3个分区,即整个消费组订阅了t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。具体而言,消费者C0订阅的是主题t0,消费者C1订阅的是主题t0和t1,消费者C2订阅的是主题t0、t1和t2,那么最终的分配结果为:
可以看到RoundRobinAssignor策略也不是十分完美,这样分配其实并不是最优解,因为完全可以将分区t1p1分配给消费者C1。
StickyAssignor分配策略
我们再来看一下StickyAssignor策略,“sticky”这个单词可以翻译为“粘性的”,Kafka从0.11.x版本开始引入这种分配策略,它主要有两个目的:
分区的分配要尽可能的均匀;
分区的分配尽可能的与上次分配的保持相同。
当两者发生冲突时,第一个目标优先于第二个目标。鉴于这两个目标,StickyAssignor策略的具体实现要比RangeAssignor和RoundRobinAssignor这两种分配策略要复杂很多。我们举例来看一下StickyAssignor策略的实际效果。
假设消费组内有3个消费者:C0、C1和C2,它们都订阅了4个主题:t0、t1、t2、t3,并且每个主题有2个分区,也就是说整个消费组订阅了t0p0、t0p1、t1p0、t1p1、t2p0、t2p1、t3p0、t3p1这8个分区。最终的分配结果如下:
这样初看上去似乎与采用RoundRobinAssignor策略所分配的结果相同,但事实是否真的如此呢?再假设此时消费者C1脱离了消费组,那么消费组就会执行再平衡操作,进而消费分区会重新分配。如果采用RoundRobinAssignor策略,那么此时的分配结果如下:
如分配结果所示,RoundRobinAssignor策略会按照消费者C0和C2进行重新轮询分配。而如果此时使用的是StickyAssignor策略,那么分配结果为:
可以看到分配结果中保留了上一次分配中对于消费者C0和C2的所有分配结果,并将原来消费者C1的“负担”分配给了剩余的两个消费者C0和C2,最终C0和C2的分配还保持了均衡。
到目前为止所分析的都是消费者的订阅信息都是相同的情况,我们来看一下订阅信息不同的情况下的处理。
举例,同样消费组内有3个消费者:C0、C1和C2,集群中有3个主题:t0、t1和t2,这3个主题分别有1、2、3个分区,也就是说集群中有t0p0、t1p0、t1p1、t2p0、t2p1、t2p2这6个分区。消费者C0订阅了主题t0,消费者C1订阅了主题t0和t1,消费者C2订阅了主题t0、t1和t2。
如果此时采用RoundRobinAssignor策略,那么最终的分配结果如下所示(和讲述RoundRobinAssignor策略时的一样,这样不妨赘述一下):
如果此时采用的是StickyAssignor策略,那么最终的分配结果为:
可以看到这是一个最优解(消费者C0没有订阅主题t1和t2,所以不能分配主题t1和t2中的任何分区给它,对于消费者C1也可同理推断)。
假如此时消费者C0脱离了消费组,那么RoundRobinAssignor策略的分配结果为:
可以看到RoundRobinAssignor策略保留了消费者C1和C2中原有的3个分区的分配:t2p0、t2p1和t2p2(针对结果集1)。而如果采用的是StickyAssignor策略,那么分配结果为:
可以看到StickyAssignor策略保留了消费者C1和C2中原有的5个分区的分配:t1p0、t1p1、t2p0、t2p1、t2p2。
从结果上看StickyAssignor策略比另外两者分配策略而言显得更加的优异,这个策略的代码实现也是异常复杂,如果读者没有接触过这种分配策略,不妨使用一下来尝尝鲜。
相关推荐
kafka分区分配策略图文详解
《Kafka分区策略详解》 Kafka作为一款高效的消息中间件,在分布式系统中扮演着重要角色。其中,分区策略是Kafka实现高并发、可扩展性的重要机制之一。本文将深入探讨Kafka的分区策略,包括Range策略和RoundRobin...
默认的分区分配策略是“Range”或“Round Robin”,但也可以自定义策略。例如,"Range"策略将分区均匀分配给消费者,而"Round Robin"策略则是轮流分配。如果需要根据特定条件(如消费者能力或地理位置)进行更精细的...
9. **Kafka 分区分配策略** Spring Boot 允许自定义分区分配策略,以决定消息如何在消费者实例间分配。默认的分区分配策略是轮询,但可以根据业务需求进行调整。 10. **错误处理与异常捕获** 当消费者处理消息时...
如果需要自定义分区分配器,可以创建一个实现了`PartitionAssignor`接口的类,并在配置中指定。例如,创建`CustomPartitionAssignor.java`: ```java public class CustomPartitionAssignor implements ...
Kafka是一个分布式流处理平台,最初由LinkedIn公司开发,后来成为Apache软件基金会的一个顶级项目。Kafka主要用于构建实时数据管道和流处理应用程序。它能够高效地处理高吞吐量的数据流,并且具有很好的可扩展性、...
- **低级API**:也称为简单或原始API,提供更多的控制,但需要开发者处理更多的细节,如手动管理分区分配和offset提交。例如,`KafkaProducer`和`KafkaConsumer`的早期版本。 在选择API时,高级API更适合大部分情况...
为了实现指定消费分区,我们可以自定义分区分配器(PartitionAssignor)。在Kafka消费者配置中,设置`partitionAssignor`属性为自定义的分配器类名。同时,自定义的分配器需要继承`AbstractPartitionAssignor`并重写...
- 分区分配策略:`kafka-python`允许用户自定义分区分配策略,以满足特定的负载均衡需求。 - 自动提交offset:高阶消费者默认会自动提交offset,但也可以手动控制或禁用此功能。 - 容错机制:库内建了重试和超时机制...
在Kafka中,有几个核心概念和机制...综上所述,理解Kafka的分区分配、消费者组的offset管理、自定义消费策略以及副本分配策略是解决Kafka常见问题的关键。正确配置和理解这些机制将有助于优化Kafka集群的性能和稳定性。
3. **分区分配策略**:Kafka-clients提供了多种分区分配策略,如`RangeAssignor`和`RoundRobinAssignor`,在源码中可以详细了解这些策略的实现细节。 二、生产者API 1. **消息发送**:`Producer.send()`方法是生产...
2. **Consumer.assign(partitions)**:设置Consumer的分区分配策略,由指定的TopicPartition列表决定。 3. **Consumer.assignment()**:返回当前的分区分配策略,即一个TopicPartition列表。 4. **Consumer.close()*...
它可以选择将消息发送到特定的分区,或者让Kafka自动分配。 4. **消费者**:消费者从主题中读取并处理消息。Kafka支持多消费者组,同一组内的消费者会通过负载均衡的方式分摊消息消费,实现水平扩展。 5. **Kafka...
- **分区分配策略**: - **RangeAssignor**:按消费者总数和分区总数整除分配,确保分区均匀。 - **RoundRobinAssignor**:循环分配策略,确保每个消费者公平获取分区。 - **StickyAssignor**:尽可能保持分区...
3. 分区分配策略:Kafka默认的分区分配策略是根据消息键的哈希值,但可以自定义策略。 4. 消费位移管理:消费者通过offset(位移)追踪消费进度,可保存在ZooKeeper或Kafka主题中。 5. 数据保留策略:Kafka可以设置...
4. 支持多主题分区分配策略:允许消费者应用自定义的分区分配策略,以满足特定的业务需求。 5. ZKFree模式:Kafka 2.4.0 开始支持不依赖于 ZooKeeper 的部署模式,减少了对 ZooKeeper 的依赖,降低了系统的复杂性。 ...
消费者组内的分区分配策略可以通过配置进行定制,比如轮询、一致性哈希等,以满足不同场景的需求。 9. 自动提交offset: 默认情况下,消费者会自动提交消费的offset到Kafka,以保持消费位置。也可以手动控制...
此库提供了生产者和消费者API,用于发送和接收消息,同时包含了元数据查询、分区分配策略等功能,确保了高效和可靠的通信。 二、C# API详解 1. 生产者API:生产者负责将消息发布到Kafka主题。Confluent.Kafka....
- **分区分配策略**:Kafka自动将主题的分区分配给消费者组内的消费者。为了在单个消费者实例中利用多线程,我们需要确保所有线程共享相同的消费者实例,而不是创建多个消费者实例。 - **线程安全**:由于多个线程将...
- 分区中的消息通过主键(Message Key)进行哈希分配,确保相同键的消息落在同一分区,实现消息顺序。 2. **C#与.NET**: - 此项目使用C#语言编写,这是微软开发的一种面向对象的编程语言,广泛应用于Windows桌面...