Spring boot 如何处理Kafka Consumer中的错误

Spring boot 如何处理Kafka Consumer中的错误,spring-boot,apache-kafka,spring-kafka,Spring Boot,Apache Kafka,Spring Kafka,我有以下Kafka配置类: @Configuration @AllArgsConstructor(access = AccessLevel.PROTECTED) public class KafkaConfiguration { private final KafkaConfigurationProperties kafkaConfigurationProperties; @Bean public ConcurrentKafkaListenerContainerFactory<Stri

我有以下Kafka配置类:

@Configuration
@AllArgsConstructor(access = AccessLevel.PROTECTED)

public class KafkaConfiguration {
private final KafkaConfigurationProperties kafkaConfigurationProperties;

@Bean
public ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> debtCollectorConsumerContainerFactory() {
     ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> factory = new ConcurrentKafkaListenerContainerFactory<>();
    factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfiguration()));
    factory.setConcurrency(kafkaConfigurationProperties.getDebtCollectorConsumerThreads());
    factory.setStatefulRetry(true);
    factory.setErrorHandler(new SeekToCurrentErrorHandler((record, exception) -> {
        if (exception instanceof SomeCustomException) {
            // here I want to mannually Acknowledge the consuming of the record
        }
    }, 10));

    ContainerProperties containerProperties = factory.getContainerProperties();
    containerProperties.setAckOnError(false);
    containerProperties.setAckMode(ContainerProperties.AckMode.RECORD);
    return factory;
}

@Bean
@Qualifier(KAFKA_LOAN_REPAYMENT_PRODUCER)
public Producer<String, RepaymentEvent> loanRepaymentProducer() {
    return new KafkaProducer<>(producerConfiguration());
}

@Bean
@Qualifier(KAFKA_DEBT_COLLECTOR_PRODUCER)
public Producer<String, RepaymentEvent> debtCollectorProducer() {
    return new KafkaProducer<>(producerConfiguration());
}

private Map<String, Object> consumerConfiguration() {
    Map<String, Object> properties = new HashMap<>();
    properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfigurationProperties.getBootstrapServers());
    properties.put(ConsumerConfig.GROUP_ID_CONFIG, kafkaConfigurationProperties.getDebtCollectorConsumerGroupId());
    properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
    properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, KafkaAvroDeserializer.class);
    properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, kafkaConfigurationProperties.getDebtCollectorConsumerAutoOffsetReset());
    properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, kafkaConfigurationProperties.getDebtCollectorConsumerMaxPollRecords());
    properties.put(KafkaAvroDeserializerConfig.SPECIFIC_AVRO_READER_CONFIG, Boolean.TRUE);
    properties.put(KafkaAvroDeserializerConfig.SCHEMA_REGISTRY_URL_CONFIG, kafkaConfigurationProperties.getConfluentEndpoint());
    properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, Boolean.FALSE);
    return properties;
}

private Map<String, Object> producerConfiguration() {
    Map<String, Object> properties = new HashMap<>();
    properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfigurationProperties.getBootstrapServers());
    properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, KafkaAvroSerializer.class);
    properties.put(KafkaAvroSerializerConfig.SCHEMA_REGISTRY_URL_CONFIG, kafkaConfigurationProperties.getConfluentEndpoint());
    return properties;
}
}
我想使用
SeekToCurrentErrorHandler
进行错误处理,我想要一些特定的东西,比如,但目前我使用
springBootVersion=2.0.4.RELEASE
springkafkafkaversion=2.1.4.RELEASE
kafkafkaversion=2.0.1
confluentVersion=3.3.1
。您能帮我设置依赖项和配置以处理Kafka consumer中的错误吗


问候

自2.0.1版以来,已提供了
seektocurinterrorHandler
。版本2.2中添加了附加功能(重试若干次后恢复)


使用Spring Boot 2.1.4和Spring for Apache Kafka 2.2.6(Boot 2.1.5将很快推出)。

自2.0.1版以来,SeektocurInterrorHandler一直可用。版本2.2中添加了附加功能(重试若干次后恢复)


使用Spring Boot 2.1.4和Spring for Apache Kafka 2.2.6(Boot 2.1.5将很快推出)。

几天后,我阅读了Gary在其他一些帖子上的回答,终于找到了解决问题的方法。也许这个问题不是很具有描述性,但是这个答案描述了我想要的行为

@配置中
我正在创建以下Springbean:

@Bean
    public ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> debtCollectorConsumerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfiguration()));
        factory.setConcurrency(kafkaConfigurationProperties.getDebtCollectorConsumerThreads());
        factory.setErrorHandler(new BlockingSeekToCurrentErrorHandler());

        ContainerProperties containerProperties = factory.getContainerProperties();
        containerProperties.setAckOnError(false);
        containerProperties.setAckMode(ContainerProperties.AckMode.RECORD);

        factory.setRetryTemplate(retryTemplate());
        return factory;
    }

private RetryTemplate retryTemplate() {
    RetryTemplate retryTemplate = new RetryTemplate();
    retryTemplate.setBackOffPolicy(backOffPolicy());
    retryTemplate.setRetryPolicy(new SimpleRetryPolicy(kafkaConfigurationProperties.getDebtCollectorConsumerRetryAttempts()));
    return retryTemplate;
}

经过几天的阅读,我终于找到了解决问题的方法。也许这个问题不是很具有描述性,但是这个答案描述了我想要的行为

@配置中
我正在创建以下Springbean:

@Bean
    public ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> debtCollectorConsumerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<String, RepaymentEvent> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(new DefaultKafkaConsumerFactory<>(consumerConfiguration()));
        factory.setConcurrency(kafkaConfigurationProperties.getDebtCollectorConsumerThreads());
        factory.setErrorHandler(new BlockingSeekToCurrentErrorHandler());

        ContainerProperties containerProperties = factory.getContainerProperties();
        containerProperties.setAckOnError(false);
        containerProperties.setAckMode(ContainerProperties.AckMode.RECORD);

        factory.setRetryTemplate(retryTemplate());
        return factory;
    }

private RetryTemplate retryTemplate() {
    RetryTemplate retryTemplate = new RetryTemplate();
    retryTemplate.setBackOffPolicy(backOffPolicy());
    retryTemplate.setRetryPolicy(new SimpleRetryPolicy(kafkaConfigurationProperties.getDebtCollectorConsumerRetryAttempts()));
    return retryTemplate;
}

您已经配置好了。您的问题到底是什么?我想使用SeekToCurrentErrorHandler()来处理异常,我发现SeekToCurrentErrorHandler的这个实现在Spring中可用于Apache Kafka 2.2。更多细节可以在发布的链接中找到。为了将Spring Kafka从2.1.4升级到2.2版本,我没有找到Spring Boot和Spring Kafka的依赖关系矩阵。您已经配置了它。您的问题到底是什么?我想使用SeekToCurrentErrorHandler()来处理异常,我发现SeekToCurrentErrorHandler的这个实现在Spring中可用于Apache Kafka 2.2。更多细节可以在发布的链接中找到。为了将SpringKafka从2.1.4升级到2.2版本,我没有找到SpringBoot和SpringKafka的依赖关系矩阵。嗨,Garry!我在问题中补充了一些细节。请给我一件安全套。大TX!恢复程序无法访问使用者,因此无法执行提交。从版本2.2.4开始,
SeekToCurrentErrorHandler
有一个新属性
commitRecovered
,只要容器配置了
AckMode.MANUAL\u IMMEDIATE
,它就会提交恢复记录的偏移量。在恢复程序中只提交某些异常是没有意义的,因为在恢复之后,该记录将被跳过,下一条记录将发送给侦听器。嗨,Gary!我已经扩展了SeekToCurrentErrorHandler类,在handle方法中,我希望在特定延迟后暂停并恢复使用者。有没有办法达到这种行为?德克萨斯州!你好,加里!我在问题中补充了一些细节。请给我一件安全套。大TX!恢复程序无法访问使用者,因此无法执行提交。从版本2.2.4开始,
SeekToCurrentErrorHandler
有一个新属性
commitRecovered
,只要容器配置了
AckMode.MANUAL\u IMMEDIATE
,它就会提交恢复记录的偏移量。在恢复程序中只提交某些异常是没有意义的,因为在恢复之后,该记录将被跳过,下一条记录将发送给侦听器。嗨,Gary!我已经扩展了SeekToCurrentErrorHandler类,在handle方法中,我希望在特定延迟后暂停并恢复使用者。有没有办法达到这种行为?德克萨斯州!
public class BlockingSeekToCurrentErrorHandler extends SeekToCurrentErrorHandler {

    private static final int MAX_RETRY_ATTEMPTS = Integer.MAX_VALUE;

    BlockingSeekToCurrentErrorHandler() {
        super(MAX_RETRY_ATTEMPTS);
    }

    @Override
    public void handle(Exception exception, List<ConsumerRecord<?, ?>> records, Consumer<?, ?> consumer, MessageListenerContainer container) {
        try {
            if (!records.isEmpty()) {
                log.warn("Exception: {} occurred with message: {}", exception, exception.getMessage());
                MetricFactory.handleDebtCollectorIncomingBlockingError(records.get(0), exception);
                super.handle(exception, records, consumer, container);
            }
        } catch (SerializationException e) {
            log.warn("Exception: {} occurred with message: {}", e, e.getMessage());
            MetricFactory.handleDebtCollectorIncomingDeserializationError(records, e);
        }
    }
}