我们正在使用spring-kafka 1.2.2.RELEASE。
我们想要的
1.一旦消息被成功使用和处理,就会在spring-kafka中提交offset。
我正在使用Manaul Commit / Acknowledgement,它工作正常。
2.如有任何例外,我们希望spring-kafka重新发送相同的消息。
我们会在任何系统错误上引发RunTime异常,该异常由spring-kafka记录,从未提交。
这很好,因为我们不希望其提交,但是该消息保留在spring-kafka中,除非重新启动服务,否则它永远不会回来。在重新启动消息返回并再次执行,然后停留在spring-kafka
我们尝试过的事情
1.我已经尝试了ErrorHandler和RetryingMessageListenerAdapter,但是在两种情况下,我们都必须在服务中编写如何再次处理消息的代码
这是我的消费者
public class MyConsumer{
@KafkaListener
public void receive(...){
// application logic to return success/failure
if(success){
acknowledgement.acknowledge();
}else{
throw new RunTimeException();
}
}
}
我对容器工厂也有以下配置
factory.getContainerProperties().setErrorHandler(new ErrorHandler(){
@Override
public void handle(...){
throw new RunTimeException("");
}
});
在执行流程时,控制权先进入内部,然后接收并处理方法。之后,该服务等待新消息。但是,我期望如此,因为我们引发了异常,并且未提交消息,因此同一条消息应该再次进入接收方法。
有什么办法,我们可以告诉spring kafka“不要提交此消息并尽快将其再次发送?”
答案 0 :(得分:1)
1.2.x不再受支持;由于KIP-62,由于1.x用户的线程模型更加简单,因此建议1.x用户至少升级到1.3.x(当前为1.3.8)。
当前版本为2.2.2。
2.0.1引入了SeekToCurrentErrorHandler
,它可以重新查找失败的记录,以便重新发送。
对于早期版本,您必须停止并重新启动容器才能重新发送失败的消息,或者将重试添加到侦听器适配器。
我建议您升级到最新版本。
答案 1 :(得分:0)
很遗憾,我们可以使用的版本是1.3.7.RELEASE。
我尝试实现ConsumerSeekAware接口。下面是我的操作方式,可以看到邮件重新发送了
消费者
public class MyConsumer implements ConsumerSeekAware{
private ConsumerSeekCallback consumerSeekCallback;
if(condition) {
acknowledgement.acknowledge();
}else {
consumerSeekCallback.seek((String)headers.get("kafka_receivedTopic"),
(int) headers.get("kafka_receivedPartitionId"),
(int) headers.get("kafka_offset"));
}
}
@Override
public void registerSeekCallback(ConsumerSeekCallback consumerSeekCallback) {
this.consumerSeekCallback = consumerSeekCallback;
}
@Override
public void onIdleContainer(Map<TopicPartition, Long> arg0, ConsumerSeekCallback arg1) {
LOGGER.debug("onIdleContainer called");
}
@Override
public void onPartitionsAssigned(Map<TopicPartition, Long> arg0, ConsumerSeekCallback arg1) {
LOGGER.debug("onPartitionsAssigned called");
}
}
配置
public class MyConsumerConfig {
@Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> props = new HashMap<>();
// Set server, deserializer, group id
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);
return props;
}
@Bean
public ConcurrentKafkaListenerContainerFactory<String, MyModel> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, MyModel> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfigs()));
factory.getContainerProperties().setAckMode(AckMode.MANUAL);
return factory;
}
@Bean
public MyConsumer receiver() {
return new MyConsumer();
}
}