Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在处理从卡夫卡检索到的每条记录之后,提交的正确方式是什么?_Java_Kotlin_Apache Kafka - Fatal编程技术网

Java 在处理从卡夫卡检索到的每条记录之后,提交的正确方式是什么?

Java 在处理从卡夫卡检索到的每条记录之后,提交的正确方式是什么?,java,kotlin,apache-kafka,Java,Kotlin,Apache Kafka,我在理解如何为我使用的每条记录正确地手动提交时遇到了一些困难 首先,让我们看一个来自 while(true){ ConsumerRecords记录=consumer.poll(100); 对于(消费者记录:记录){ buffer.add(记录); } if(buffer.size()>=minBatchSize){ insertIntoDb(缓冲区); consumer.commitSync(); buffer.clear(); } } 此示例仅在处理轮询中收到的所有记录后提交。我认为这不是一

我在理解如何为我使用的每条记录正确地手动提交时遇到了一些困难

首先,让我们看一个来自

while(true){
ConsumerRecords记录=consumer.poll(100);
对于(消费者记录:记录){
buffer.add(记录);
}
if(buffer.size()>=minBatchSize){
insertIntoDb(缓冲区);
consumer.commitSync();
buffer.clear();
}
}
此示例仅在处理轮询中收到的所有记录后提交。我认为这不是一个很好的方法,因为如果我们收到三条记录,而我的服务在处理第二条记录时死亡,它将再次消耗第一条记录,这是不正确的

因此,还有第二个示例介绍了在每个分区的基础上提交记录:

try {
     while(running) {
         ConsumerRecords<String, String> records = consumer.poll(Long.MAX_VALUE);
         for (TopicPartition partition : records.partitions()) {
             List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);
             for (ConsumerRecord<String, String> record : partitionRecords) {
                 System.out.println(record.offset() + ": " + record.value());
             }
             long lastOffset = partitionRecords.get(partitionRecords.size() - 1).offset();
             consumer.commitSync(Collections.singletonMap(partition, new OffsetAndMetadata(lastOffset + 1)));
         }
     }
 } finally {
   consumer.close();
 }
试试看{
(跑步时){
ConsumerRecords记录=consumer.poll(Long.MAX_值);
for(TopicPartition分区:records.partitions()){
列表分区记录=记录。记录(分区);
for(消费者记录记录:分区记录){
System.out.println(record.offset()+“:”+record.value());
}
long lastOffset=partitionRecords.get(partitionRecords.size()-1.offset();
consumer.commitSync(Collections.singletonMap(分区、新偏移量和元数据(lastOffset+1));
}
}
}最后{
consumer.close();
}
但是,我认为它也有同样的问题,它只在处理来自特定分区的所有记录之后才提交

我想出的解决方案是:

        val consumer: Consumer<String, MyEvent> = createConsumer(bootstrap)
        consumer.subscribe(listOf("some-topic"))

        while (true) {
            val records: ConsumerRecords<String, MyEvent> = consumer.poll(Duration.ofSeconds(1))
            if (!records.isEmpty) {
                mainLogger.info("Received ${records.count()} events from CRS kafka topic, with partitions ${records.partitions()}")
                records.forEach {
                    mainLogger.debug("Record at offset ${it.offset()}, ${it.value()}")
                    processEvent(it.value()) // Complex event processing occurs in this function
                    consumer.commitSync(mapOf(TopicPartition(it.topic(), it.partition()) to OffsetAndMetadata (it.offset() + 1)))
                }
            }
        }
val consumer:consumer=createConsumer(引导)
consumer.subscribe(某些主题的列表)
while(true){
val记录:ConsumerRecords=consumer.poll(持续时间为秒(1))
如果(!records.isEmpty){
mainLogger.info(“从CRS kafka主题接收到${records.count()}事件,分区${records.partitions()}”)
forEach记录{
调试(“偏移量${it.offset()},${it.value()}处的记录”)
processEvent(it.value())//此函数中发生复杂事件处理
consumer.commitSync(将TopicPartition(it.topic(),it.partition())映射到OffsetAndMetadata(it.offset()+1)))
}
}
}
现在,在我测试时,这似乎起作用了。到目前为止,在我的测试过程中,似乎只有一个分区被使用(我已经通过记录records.partitions()检查了这一点)


这种方法会引起任何问题吗?消费者API似乎不提供在不指定分区的情况下提交偏移量的方法,这对我来说似乎有点奇怪。我在这里遗漏了什么吗?

没有正确或错误的方法来犯错误。这实际上取决于您的用例和应用程序

提交每个偏移量可以提供更细粒度的控制,但它会影响性能。另一方面,您可以每X秒异步提交一次(就像自动提交一样),开销很小,但控制更少


在第一个示例中,事件被批处理和提交。就性能而言,这很有趣,但如果出现错误,可以重新处理整个批次

在第二个示例中,它也是批处理,但仅针对每个分区。这将导致更小的批次,从而降低性能,但在出现问题时减少再处理

在上一个示例中,选择提交每条消息。虽然这提供了最多的控制,但它会显著影响性能。此外,与其他情况一样,它不是完全防错的

如果应用程序在事件处理后但在提交之前崩溃,则在重新启动时,最后一个事件可能会被重新处理(即至少一次)。但至少只有一个事件会受到影响


如果只需要一次语义,则需要使用。

这至少会导致处理缓慢的问题。这就是为什么会有批提交,甚至异步偏移量提交。所有这些都是为了提高吞吐量。重复处理的可能性也会出现,但它通常被认为不如吞吐量低那么重要,特别是因为许多其他消息传递系统在设计上无法保证消息的“精确一次”传递,许多消息传递系统只能保证“至少一次”。谢谢,这很有意义。我想我更想知道,当存在多个分区时,我在这里使用的方法是否会导致任何问题。必须构造自己的主题分区才能提交对我来说似乎是一种代码味道。
        val consumer: Consumer<String, MyEvent> = createConsumer(bootstrap)
        consumer.subscribe(listOf("some-topic"))

        while (true) {
            val records: ConsumerRecords<String, MyEvent> = consumer.poll(Duration.ofSeconds(1))
            if (!records.isEmpty) {
                mainLogger.info("Received ${records.count()} events from CRS kafka topic, with partitions ${records.partitions()}")
                records.forEach {
                    mainLogger.debug("Record at offset ${it.offset()}, ${it.value()}")
                    processEvent(it.value()) // Complex event processing occurs in this function
                    consumer.commitSync(mapOf(TopicPartition(it.topic(), it.partition()) to OffsetAndMetadata (it.offset() + 1)))
                }
            }
        }