使用DeadLetterPublishingRecoverer的状态重试导致RetryCacheCapacityExceededException

时间:2019-07-02 06:57:22

标签: spring-kafka

我的容器工厂有一个SeekToCurrentErrorHandler,它使用DeadLetterPublishingRecoverer将某些“ NotRetryableException”类型的异常发布到DLT,并为其他种类的异常无限次地寻找相同的偏移量。通过此设置,在一定数量的有效负载导致不可重试的异常之后,存储重试上下文的映射-MapRetryContextCache(spring-retry)溢出,并抛出RetryCacheCapacityExceededException。从最初的外观看,不会从MapRetryContextCache中删除将由DLT恢复器处理的消息的重试上下文。要么是我的配置不正确。

SeekToCurrentErrorHandler eh = new SeekToCurrentErrorHandler(
                new DeadLetterPublishingRecoverer(kafkaTemplate),-1);
eh.addNotRetryableException(SomeNonRetryableException.class);
        eh.setCommitRecovered(true);
        ConcurrentKafkaListenerContainerFactory<String, String> factory
                = getContainerFactory();
        factory.setErrorHandler(eh);
        factory.setRetryTemplate(retryTemplate);
        factory.setStatefulRetry(true);

1 个答案:

答案 0 :(得分:0)

为了清除缓存,必须在重试模板中而不是在错误处理程序中进行恢复。

@SpringBootApplication
public class So56846940Application {

    public static void main(String[] args) {
        SpringApplication.run(So56846940Application.class, args);
    }

    @Bean
    public NewTopic topic() {
        return TopicBuilder.name("so56846940").partitions(1).replicas(1).build();
    }

    @Bean
    public NewTopic topicDLT() {
        return TopicBuilder.name("so56846940.DLT").partitions(1).replicas(1).build();
    }

    @Bean
    public ApplicationRunner runner(KafkaTemplate<String, String> template,
            ConcurrentKafkaListenerContainerFactory<String, String> factory,
            DeadLetterPublishingRecoverer recoverer) {

        factory.setRetryTemplate(new RetryTemplate());
        factory.setStatefulRetry(true);
        factory.setRecoveryCallback(context -> {
            recoverer.accept((ConsumerRecord<?, ?>) context.getAttribute("record"),
                    (Exception) context.getLastThrowable());
            return null;
        });

        return args -> IntStream.range(0, 5000).forEach(i -> template.send("so56846940", "foo"));
    }

    @KafkaListener(id = "so56846940", topics = "so56846940")
    public void listen(String in) {
        System.out.println(in);
        throw new RuntimeException();
    }

    @Bean
    public DeadLetterPublishingRecoverer recoverer(KafkaTemplate<String, String> template) {
        return new DeadLetterPublishingRecoverer(template);
    }

    @Bean
    public SeekToCurrentErrorHandler eh() {
        return new SeekToCurrentErrorHandler(4);
    }

}

错误处理程序必须重试至少与重试模板一样多次,以使重试用尽并清除缓存。

还应该为RetryTemplate配置与错误处理程序相同的不可重试的异常。

我们将在参考手册中进行说明。