这是对Reading the same message several times from Kafka的后续问题。如果有更好的方式在不发布新问题的情况下提出这个问题,请告诉我。在这篇文章中,加里提到了
“但如果已经检索过邮件,你仍然会先看到以后的邮件,所以你也必须丢弃它们。”
在调用seek()之后,是否有一种干净的方法来丢弃poll()已读取的消息?我开始实现逻辑来保存初始偏移量(在recordOffset中),并在成功时递增它。失败时,我调用seek()并将recordOffset的值设置为record.offset()。然后,对于每个新消息,我检查以查看record.offset()是否大于recordOffset。如果是,我只需调用acknowledge(),从而“丢弃”所有先前读取的消息。这是代码 -
// in onMessage()...
if (record.offset() > recordOffset){
acknowledgment.acknowledge();
return;
}
try {
processRecord(record);
recordOffset = record.offset()+1;
acknowledgment.acknowledge();
} catch (Exception e) {
recordOffset = record.offset();
consumerSeekCallback.seek(record.topic(), record.partition(), record.offset());
}
这种方法的问题在于它使多个分区变得复杂。有更简单/更清洁的方式吗?
编辑1 根据Gary的建议,我尝试添加一个像这样的errorHandler -
@KafkaListener(topicPartitions =
{@org.springframework.kafka.annotation.TopicPartition(topic = "${kafka.consumer.topic}", partitions = { "1" })},
errorHandler = "SeekToCurrentErrorHandler")
这种语法是否有问题,因为我得到“无法解决方法'errorHandler'”?
编辑2 在Gary解释了2个错误处理程序之后,我删除了上面的errorHandler并将其添加到配置文件中 -
@Bean
public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory();
factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(kafkaProps()));
factory.getContainerProperties().setAckOnError(false);
factory.getContainerProperties().setErrorHandler(new SeekToCurrentErrorHandler());
factory.getContainerProperties().setAckMode(AbstractMessageListenerContainer.AckMode.MANUAL);
return factory;
}
当我启动应用程序时,我现在收到此错误...
java.lang.NoSuchMethodError:org.springframework.util.Assert.state(ZLjava / util / function / Supplier;)V 在org.springframework.kafka.listener.adapter.MessagingMessageListenerAdapter.determineInferredType(MessagingMessageListenerAdapter.java:396)
这是第396行 -
Assert.state(!this.isConsumerRecordList || validParametersForBatch,
() -> String.format(stateMessage, "ConsumerRecord"));
Assert.state(!this.isMessageList || validParametersForBatch,
() -> String.format(stateMessage, "Message<?>"));
答案 0 :(得分:3)
从版本2.0.1开始,如果容器的ErrorHandler
是RemainingRecordsErrorHandler
,例如SeekToCurrentErrorHandler
,则剩余的记录(包括失败的记录)将被发送到错误处理程序而不是听众。
这允许SeekToCurrentErrorHandler
重新定位每个分区,以便下一个轮询将返回未处理的记录。
/**
* An error handler that seeks to the current offset for each topic in the remaining
* records. Used to rewind partitions after a message failure so that it can be
* replayed.
*
* @author Gary Russell
* @since 2.0.1
*
*/
public class SeekToCurrentErrorHandler implements RemainingRecordsErrorHandler
修改强>
有两种类型的错误处理程序。 KafkaListenerErrorHandler
(在注释中指定)在侦听器级别工作;它连接到调用@KafkaListener
注释的侦听器适配器,因此只能访问当前记录。
第二个错误处理程序(在侦听器容器上配置)在容器级别工作,因此可以访问其余记录。 SeekToCurrentErrorHandler
是容器级错误处理程序。
它在容器工厂中的容器属性上配置...
@Bean
public ConcurrentKafkaListenerContainerFactory kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory factory = new ConcurrentKafkaListenerContainerFactory();
factory.setConsumerFactory(this.consumerFactory);
factory.getContainerProperties().setAckOnError(false);
factory.getContainerProperties().setErrorHandler(new SeekToCurrentErrorHandler());
factory.getContainerProperties().setAckMode(AckMode.RECORD);
return factory;
}