背景
有一次看到kafka的rebalance,觉得挺有趣,于是自己动手实验一下
实验设计
设计一个包含3个分区的主题,分别验证消费者1,2,3,4个时,分区和消费者的关系
实验步骤
- 创建一个包含3个分区的主题
//创建TopicName为topic-partition3的Topic并设置分区数为3以及副本数为1
@Bean
public NewTopic initialTopic() {
return new NewTopic("topic-partition3",3, (short) 1 );
}
- 一个生产者每1000ms往3个分区各生产一条数据
@Test
public void testProducer() throws InterruptedException {
for (int i = 0; i < 5000; i++) {
kafkaTemplate.send("topic-partition3", 0, null, "data package from partition0 [" + i + "]");
kafkaTemplate.send("topic-partition3", 1, null, "data package from partition1 [" + i + "]");
kafkaTemplate.send("topic-partition3", 2, null, "data package from partition2 [" + i + "]");
//休眠1秒
Thread.sleep(1000);
}
}
- 同一消费者组内逐条消费数据,每条耗时800ms
//声明consumerID为ypq-consumer, 消费组ypq-group, 为监听topicName为topic-partition3的Topic
@KafkaListener(id = "ypq-consumer", groupId = "ypq-group", topics = "topic-partition3")
public void listen(String msgData) throws InterruptedException {
LOGGER.info("consume data : " + msgData);
// 模拟耗时处理
Thread.sleep(800);
}
- 仅一个消费者时,C1打印
ypq-group: partitions assigned: [topic-partition3-1, topic-partition3-2, topic-partition3-0]
- 两个消费者时,C1打印
ypq-group: partitions assigned: [topic-partition3-1, topic-partition3-0]
, C2打印ypq-group: partitions assigned: [topic-partition3-2]
- 三个消费者,C1打印
ypq-group: partitions assigned: [topic-partition3-0]
,C2打印ypq-group: partitions assigned: [topic-partition3-1]
, C3打印ypq-group: partitions assigned: [topic-partition3-2]
- 四个消费者时, C1-3打印不变, C4打印
ypq-group: partitions assigned: []
实验统计
消费者个数 | P1 | P2 | P3 |
---|---|---|---|
1 | C1 | C1 | C1 |
2 | C1 | C1 | C2 |
3 | C1 | C2 | C3 |
4 | C1 | C2 | C3 |
总结
- 消费者数大于分区数时,产生空闲的消费者,因为每个分区最多只能被一个消费者消费
- Kafka的rebalance默认策略是range,分区数对消费者数求余的余数会分配给靠前的消费者, 所以靠前消费者压力较大
- 同一topic下不同分区的数据不能保证有序, 同一分区下的数据保证有序
- 一个消费者如果要多线程消费, 需自行保证多线程消费的数据顺序
- 消费者还需考虑重复数据消费的场景