卡夫卡:增加的分区无法在下一次重新平衡时分配

时间:2018-10-17 07:18:57

标签: apache-kafka kafka-consumer-api

我遇到了有关Kafka重新平衡的奇怪事情。如果我增加某个Java使用者(在同一组中)订阅的主题的分区,则不会发生使用者重新平衡。之后,我尝试通过启动新使用者(或杀死一个使用者)来引起重新平衡,并且无法在此重新平衡中分配新增加的分区。我发现只有在停止所有使用者并启动它们之后才能分配新分区。我不知道这是正常现象还是对此有任何解释。

以下是我在计算机上进行的测试:

1。启动ZK卡夫卡。创建一个具有1个分区的普通主题( test-topic

./bin/kafka-topics.sh --zookeeper 127.0.0.1:2181 --create --topic test-topic --partitions 1 --replication-factor 1 --config retention.ms=604800000

2。启动2个Java使用者( C1 C2 ),订阅 test-topic

3。增加2个 test-topic

分区
$ ./bin/kafka-topics.sh --zookeeper 127.0.0.1:2181 --alter --topic test-topic --partitions 3

C1 C2

中没有发生重新平衡

4。启动新的使用者 C3 以订阅测试主题。发生重新平衡,但仅涉及重新分配的分区 test-topic-0 ,没有涉及 test-topic-1 test-topic-2

5。我尝试通过停止 C2 C3 来引起重新平衡。但是 test-topic-1 test-topic-2 仍未分配。

6。停止所有正在运行的使用者,然后启动它们。所有 test-topic-0,1,2 均已正常分配。

kafka和java api版本:kafka_2.12-2.0.0(我也尝试过kafka_2.11-1.0.0和kafka_2.10-0.10.2.1,结果相同)

动物园管理员:3.4.13

消费者代码

public class KafkaConsumerThread extends Thread {
    // consumer settings
    public static org.apache.kafka.clients.consumer.KafkaConsumer<String, String> createNativeConsumer(String groupName, String kafkaBootstrap) {
        Properties props = new Properties();
        props.put("bootstrap.servers", kafkaBootstrap);
        props.put("group.id", groupName);
        props.put("auto.offset.reset", "earliest");
        props.put("enable.auto.commit", true);
        props.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");
        return new KafkaConsumer<String, String>(props);
    }

    @Override
    public void run() {
        log.info("Start consumer ..");
        consumer.subscribe(Collections.singleton(topicName), consumerRebalanceListener);
        while (!stop) {
            try {
                ConsumerRecords<String, String> records = consumer.poll(100);
                receivedRecordNumber.addAndGet(records.count());
                Iterator<ConsumerRecord<String, String>> iterator = records.iterator();
                while (iterator.hasNext()) {
                    ConsumerRecord<String, String> record = iterator.next();
                    log.info("Receive [key:{}][value:{}]", record.key(), record.value());
                }
            } catch (TimeoutException e) {
                log.info("no data");
            }
        }
        consumer.close();
    }
}

感谢@Aftab Virtual的评论。我再次测试,等待更长的时间。第一个使用者启动约5分钟后,自动重新分配平衡,并重新​​分配了所有分区 test-topic-0,1,2 。因此,更改分区后,Kafka确实具有自动重新平衡的功能。

此外,我遵循@Aftab Virtual的建议将leader.imbalance.check.interval.seconds更改为30。但是,重新平衡涉及的所有分区都在分区增加后约3分钟发生。我确实为代理添加了设置:

auto.leader.rebalance.enable = true
leader.imbalance.check.interval.seconds = 30

我不知道这种重新平衡的机制是什么。而且没有更多的日志可用于这种重新平衡:

[2018-10-18 11:32:47,958] INFO [GroupCoordinator 0]: Preparing to rebalance group test-group with old generation 4 (__consumer_offsets-12) (kafka.coordinator.group.GroupCoordinator)
[2018-10-18 11:32:50,963] INFO [GroupCoordinator 0]: Stabilized group test-group generation 5 (__consumer_offsets-12) (kafka.coordinator.group.GroupCoordinator)
[2018-10-18 11:32:50,964] INFO [GroupCoordinator 0]: Assignment received from leader for group test-group for generation 5 (kafka.coordinator.group.GroupCoordinator)

1 个答案:

答案 0 :(得分:0)

在征求Kafka团队和一些Kafka用户的建议后,我得到了测试结果的解释。这不是错误。

分区的增加将标记为metadata.updateNeeded = true。但是,这不会真正触发更新,直到下一个元数据到期时间为止(默认metadata.max.age.ms为5 * 60 * 1000 ms)。在组长更新其元数据之前,由更改消费者编号引起的重新平衡将不涉及新分区。

我将metadata.max.age.ms减少到30秒,Kafka对分区增加变得更加敏感。