Kafka:从主题消费第一条消息时间歇性缓慢

时间:2016-07-22 19:39:14

标签: apache-kafka

我正在使用Kafka 0.9.0.1。

我第一次启动应用程序时需要20-30秒才能检索到最新的"来自主题的消息

我使用过不同的Kafka经纪人(使用不同的配置)但我仍然看到这种行为。后续消息通常没有缓慢。

这是预期的行为吗?您可以通过运行此示例应用程序并将代理/主题名称更改为您自己的设置来清楚地看到以下内容

public class KafkaProducerConsumerTest {

    public static final String KAFKA_BROKERS = "...";
    public static final String TOPIC = "...";

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new KafkaProducerConsumerTest().run();
    }

    public void run() throws ExecutionException, InterruptedException {
        Properties consumerProperties = new Properties();
        consumerProperties.setProperty("bootstrap.servers", KAFKA_BROKERS);
        consumerProperties.setProperty("group.id", "Test");
        consumerProperties.setProperty("auto.offset.reset", "latest");
        consumerProperties.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        consumerProperties.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        MyKafkaConsumer kafkaConsumer = new MyKafkaConsumer(consumerProperties, TOPIC);
        Executors.newFixedThreadPool(1).submit(() -> kafkaConsumer.consume());

        Properties producerProperties = new Properties();
        producerProperties.setProperty("bootstrap.servers", KAFKA_BROKERS);
        producerProperties.setProperty("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        producerProperties.setProperty("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        MyKafkaProducer kafkaProducer = new MyKafkaProducer(producerProperties, TOPIC);
        kafkaProducer.publish("Test Message");
    }
}


class MyKafkaConsumer {
    private final Logger logger = LoggerFactory.getLogger(MyKafkaConsumer.class);
    private KafkaConsumer<String, Object> kafkaConsumer;

    public MyKafkaConsumer(Properties properties, String topic) {
        kafkaConsumer = new KafkaConsumer<String, Object>(properties);
        kafkaConsumer.subscribe(Lists.newArrayList(topic));
    }

    public void consume() {
        while (true) {
            logger.info("Started listening...");
            ConsumerRecords<String, Object> consumerRecords = kafkaConsumer.poll(Long.MAX_VALUE);
            logger.info("Received records {}", consumerRecords.iterator().next().value());
        }
    }
}

class MyKafkaProducer {
    private KafkaProducer<String, Object> kafkaProducer;
    private String topic;

    public MyKafkaProducer(Properties properties, String topic) {
        this.kafkaProducer = new KafkaProducer<String, Object>(properties);
        this.topic = topic;
    }

    public void publish(Object object) throws ExecutionException, InterruptedException {
        ProducerRecord<String, Object> producerRecord = new ProducerRecord<>(topic, "key", object);
        Future<RecordMetadata> response = kafkaProducer.send(producerRecord);
        response.get();
    }

}

3 个答案:

答案 0 :(得分:3)

第一条消息应该花费比其余消息更长的时间,因为当您在语句consumerProperties.setProperty("group.id", "Test");指定的使用者组中启动新的消费者时,Kakfka将平衡分区,使得每个分区都被最多一个消费者使用,并且在多个消费者流程中分配主题的分区。

此外,对于Kafka 0.9,还有一个单独的__consumer_offsets主题,Kafka用它来管理消费者群体中每个消费者的偏移量。当您第一次启动消费者时,它可能会查看此主题以获取最新的偏移量(可能有消费者在此之前使用此主题而导致其被杀死,因此需要从正确的抵消)。

这两个因素将导致第一组消息的消耗延迟更高。我无法评论20-30秒的确切延迟,但我想这应该是默认行为。

PS:具体数字还可能取决于其他次要因素,例如您是否正在运行经纪人和同一台机器上的消费者(没有网络延迟)或者使用TCP进行通信的不同消费者。

答案 1 :(得分:0)

现在多次尝试使用最少的日志记录添加代码。这是典型的日志输出:

2016-07-24 15:12:51,417 Start polling...|INFO|KafkaProducerConsumerTest
2016-07-24 15:12:51,604 producer has send message|INFO|KafkaProducerConsumerTest
2016-07-24 15:12:51,619 producer got response, exiting|INFO|KafkaProducerConsumerTest
2016-07-24 15:12:51,679 Received records [Test Message]|INFO|KafkaProducerConsumerTest
2016-07-24 15:12:51,679 Start polling...|INFO|KafkaProducerConsumerTest
2016-07-24 15:12:54,680 returning on empty poll result|INFO|KafkaProducerConsumerTest

事件的顺序如预期和及时。消费者开始轮询,生产者发送消息并接收结果,消费者接收消息并且所有这些都是300ms。然后消费者再次开始轮询,并在3秒后被抛出,因为我分别更改了轮询超时。

我将Kafka 0.9.0.1用于代理和客户端库。连接在localhost上,它是一个完全没有负载的测试环境。

为了完整性,以下是上述交换所触发的服务器的日志形式。

[2016-07-24 15:12:51,599] INFO [GroupCoordinator 0]: Preparing to restabilize group Test with old generation 0 (kafka.coordinator.GroupCoordinator)
[2016-07-24 15:12:51,599] INFO [GroupCoordinator 0]: Stabilized group Test generation 1 (kafka.coordinator.GroupCoordinator)
[2016-07-24 15:12:51,617] INFO [GroupCoordinator 0]: Assignment received from leader for group Test for generation 1 (kafka.coordinator.GroupCoordinator)
[2016-07-24 15:13:24,635] INFO [GroupCoordinator 0]: Preparing to restabilize group Test with old generation 1 (kafka.coordinator.GroupCoordinator)
[2016-07-24 15:13:24,637] INFO [GroupCoordinator 0]: Group Test generation 1 is dead and removed (kafka.coordinator.GroupCoordinator)

您可能希望与同一交换的服务器日志进行比较。

答案 2 :(得分:0)

根据this link

  

尝试在您的消费者中设置group_id=None,或致电 consumer.close()   在结束脚本之前,或使用assign()而不是subscribe()。否则你就是   重新加入已知但无响应的成员的现有组。该   小组协调员将等待,直到这些成员签入/离开/超时。   由于消费者不再存在(他们之前的脚本运行),他们有   超时   并且consumer.poll()在组重新平衡期间阻止。

因此,如果您以无响应的成员加入群组(也许您不合理地终止该申请),这是正确的行为。

请确认您致电&#34; consumer.close()&#34;在退出申请之前。