Spring boot 春季卡夫卡-手动确认

Spring boot 春季卡夫卡-手动确认,spring-boot,apache-kafka,spring-kafka,Spring Boot,Apache Kafka,Spring Kafka,我有一个spring引导应用程序,它监听Kafka流,并将记录发送给某个服务进行进一步处理。服务有时可能会失败。评论中提到了例外情况。到目前为止,我自己模拟了服务成功和异常场景 侦听器代码: @Autowired PlanitService service @KafkaListener( topics = "${app.topic}", groupId = "notifGrp", containerFactory = "storeKafkaLi

我有一个spring引导应用程序,它监听Kafka流,并将记录发送给某个服务进行进一步处理。服务有时可能会失败。评论中提到了例外情况。到目前为止,我自己模拟了服务成功和异常场景

侦听器代码:

@Autowired
PlanitService service

@KafkaListener(
        topics = "${app.topic}",
        groupId = "notifGrp", 
        containerFactory = "storeKafkaListener")
public void processStoreNotify(StoreNotify store) throws RefrigAlarmNotifyException{
       service.planitStoreNotification(store);

       // Some other logic which throws custom exception
       // RefrigAlarmNotifyException


    }
}
消费者工厂配置如下所示:

@Bean
    public ConsumerFactory<String, StoreNotify> storeConsumerFactory() {
        Map<String, Object> config = new HashMap<>();
        config.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getConsumerBootstrapServers());
        config.put(ConsumerConfig.GROUP_ID_CONFIG, "notifGrp");
        config.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        config.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        config.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");

        try (ErrorHandlingDeserializer2<String> headerErrorHandlingDeserializer = new ErrorHandlingDeserializer2<>(
                new StringDeserializer());
                ErrorHandlingDeserializer2<StoreNotify> errorHandlingDeserializer = new ErrorHandlingDeserializer2<>(
                        new JsonDeserializer<>(StoreNotify.class, objectMapper()))) {
            return new DefaultKafkaConsumerFactory<>(config, headerErrorHandlingDeserializer,
                    errorHandlingDeserializer);
        }
    }

    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, StoreNotify> storeKafkaListener() {
        ConcurrentKafkaListenerContainerFactory<String, StoreNotify> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(storeConsumerFactory());
        factory.getContainerProperties().setAckOnError(false);
        factory.getContainerProperties().setAckMode(AckMode.RECORD);
        //factory.setMessageConverter(new ByteArrayJsonMessageConverter());     

        DeadLetterPublishingRecoverer recoverer = new DeadLetterPublishingRecoverer(kafkaTemplate,
                (r, e) -> {

                    LOGGER.error("Exception is of type: ", e);
                    if (e instanceof RestClientException) {
                        LOGGER.error("RestClientException while processing {} ", r.value(), e);
                        return new TopicPartition(storeDeadLtrTopic, r.partition());
                    }
                    else {
                        LOGGER.error("Generic exception while processing {} ", r.value(), e);
                        return new TopicPartition(storeErrorTopic, r.partition());
                    }
                });
        factory.setErrorHandler(new SeekToCurrentErrorHandler(recoverer, new FixedBackOff(0L, 0L)));
        return factory;
    }

对于这个用例,您不需要使用手动确认;只需配置一个
SeekToCurrentErrorHandler
,并向容器抛出异常;它将丢弃未处理的记录,执行查找并重新传递失败的消息

您可以使用
死信发布恢复程序
配置错误处理程序,该程序可用于在多次重试后将记录发送到死信主题

您可以配置哪些异常是可重试的

        } catch (Exception exception) {
            LOGGER.error("Exception while calling the service  ", exception);
            // Ignore the record
        }
您不能像那样“吃”异常,让它传播到容器中


使用手动确认时,您必须添加
确认
作为参数并进行确认。

谢谢您提供的信息。我将采用这种方法。你知道为什么现有的代码不起作用吗。在上面的代码中,我忘了将确认作为一个参数,但在实际代码中它就在那里。因为您正在捕获异常,所以容器对此一无所知。即使您抛出了异常,默认错误处理程序也会记录异常并继续。是的,这很有意义。请参阅。如果添加
SeekToCurrentErrorHandler
,则可以配置1)根据异常类型重试多少次,以及2)在进行下一次传递尝试之前延迟多长时间。这允许您控制哪些异常是致命的,不应重试。默认情况下,有10次无回退的交付尝试。默认情况下,某些异常被认为是致命的。同样,请参阅参考手册。重试时,您可以记录或发送到另一个主题。顶级异常是
ListenerExecutionFailedException
您需要
if(e.getCause()instanceof RestClientException){
        } catch (Exception exception) {
            LOGGER.error("Exception while calling the service  ", exception);
            // Ignore the record
        }