如何检查Kafka Consumer是否准备就绪

时间:2018-01-03 06:03:10

标签: apache-kafka rebalancing

我将Kafka提交策略设置为最新且缺少前几条消息。如果我在开始将消息发送到输入主题之前给出20秒的睡眠,则一切都按预期工作。我不确定消费者是否需要花费很长时间来进行分区重新平衡。有没有办法在开始投票之前知道消费者是否准备好了?

5 个答案:

答案 0 :(得分:0)

如果你的政策设置为最新 - 如果没有以前提交的抵消也会生效 - 但你之前没有承诺的抵消,那么你不应该担心'丢失'的消息,因为你告诉Kafka不要关心关于“先前”发送给您的消费者的消息准备就绪。

如果您关心“以前的”消息,则应将策略设置为最早。

在任何情况下,无论政策如何,您所看到的行为都是暂时的,即一旦承诺的抵消被保存在Kafka中,每次重启时消费者都会在他们离开前的地方找到

答案 1 :(得分:0)

  • 您可以使用consumer.assignment(),它将返回一组分区,并验证是否分配了可用于该主题的所有分区。

  • 如果您使用的是spring-kafka项目,则可以包含spring-kafka-test dependancy并使用以下方法等待主题分配,但您需要拥有容器。 ContainerTestUtils.waitForAssignment(Object container, int partitions);

答案 2 :(得分:0)

您可以执行以下操作:

我有一个从kafka主题读取数据的测试。
因此,您不能在多线程环境中使用KafkaConsumer,但可以传递参数“ AtomicReference分配”,在使用者线程中对其进行更新,并在另一个线程中进行读取。

例如,摘录项目中的工作代码以进行测试:

    private void readAvro(String readFromKafka,
                      AtomicBoolean needStop,
                      List<Event> events,
                      String bootstrapServers,
                      int readTimeout) {
    // print the topic name
    AtomicReference<Set<TopicPartition>> assignment = new AtomicReference<>();
    new Thread(() -> readAvro(bootstrapServers, readFromKafka, needStop, events, readTimeout, assignment)).start();

    long startTime = System.currentTimeMillis();
    long maxWaitingTime = 30_000;
    for (long time = System.currentTimeMillis(); System.currentTimeMillis() - time < maxWaitingTime;) {
        Set<TopicPartition> assignments = Optional.ofNullable(assignment.get()).orElse(new HashSet<>());
        System.out.println("[!kafka-consumer!] Assignments [" + assignments.size() + "]: "
                + assignments.stream().map(v -> String.valueOf(v.partition())).collect(Collectors.joining(",")));
        if (assignments.size() > 0) {
            break;
        }
        try {
            Thread.sleep(1_000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            needStop.set(true);
            break;
        }
    }
    System.out.println("Subscribed! Wait summary: " + (System.currentTimeMillis() - startTime));
}

private void readAvro(String bootstrapServers,
                      String readFromKafka,
                      AtomicBoolean needStop,
                      List<Event> events,
                      int readTimeout,
                      AtomicReference<Set<TopicPartition>> assignment) {

    KafkaConsumer<String, byte[]> consumer = (KafkaConsumer<String, byte[]>) queueKafkaConsumer(bootstrapServers, "latest");
    System.out.println("Subscribed to topic: " + readFromKafka);
    consumer.subscribe(Collections.singletonList(readFromKafka));

    long started = System.currentTimeMillis();
    while (!needStop.get()) {
        assignment.set(consumer.assignment());
        ConsumerRecords<String, byte[]> records = consumer.poll(1_000);
        events.addAll(CommonUtils4Tst.readEvents(records));

        if (readTimeout == -1) {
            if (events.size() > 0) {
                break;
            }
        } else if (System.currentTimeMillis() - started > readTimeout) {
            break;
        }
    }

    needStop.set(true);

    synchronized (MainTest.class) {
        MainTest.class.notifyAll();
    }
    consumer.close();
}

P.S。
needStop -全局标志,如果成功失败,则停止所有正在运行的线程
事件-我要检查的对象列表
readTimeout -我们将等待多少时间才能读取所有数据,如果readTimeout == -1,然后在读取任何内容时停止

答案 3 :(得分:0)

感谢Alexey(我也投了赞成票),我似乎基本上按照相同的想法解决了我的问题。

只想分享我的经验...就我们而言,我们以请求和响应方式使用Kafka,有点像RPC。正在针对一个主题发送请求,然后等待对另一个主题的响应。遇到类似的问题,即错过了第一反应。

我已经反复尝试... KafkaConsumer.assignment();(使用Thread.sleep(100);),但似乎没有帮助。添加一个KafkaConsumer.poll(50);似乎已经对使用者(组)进行了初始化,并且也收到了第一个响应。经过几次测试,现在可以正常使用了。

顺便说一句,测试需要停止应用程序并删除Kafka主题,并且在很好的情况下,也需要重新启动Kafka。

PS:仅调用poll(50);而没有获取assignment();的逻辑(如Alexey所述)可能无法保证消费者(组)已准备就绪。

答案 4 :(得分:0)

您可以修改 AlwaysSeekToEndListener(仅收听新消息)以包含回调:

public class AlwaysSeekToEndListener<K, V> implements ConsumerRebalanceListener {
    private final Consumer<K, V> consumer;
    private Runnable callback;

    public AlwaysSeekToEndListener(Consumer<K, V> consumer) {
        this.consumer = consumer;
    }

    public AlwaysSeekToEndListener(Consumer<K, V> consumer, Runnable callback) {
        this.consumer = consumer;
        this.callback = callback;
    }

    @Override
    public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
    }

    @Override
    public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
        consumer.seekToEnd(partitions);
        if (callback != null) {
            callback.run();
        }
    }
}

并使用闩锁回调订阅:

CountDownLatch initLatch = new CountDownLatch(1);

consumer.subscribe(singletonList(topic), new AlwaysSeekToEndListener<>(consumer, () -> initLatch.countDown()));

initLatch.await(); // blocks until consumer is ready and listening

然后继续启动您的生产者。

相关问题