Spring Kafka Retry是否在不同分区之间选择消息?

时间:2019-04-25 21:16:56

标签: spring-kafka spring-retry

我想知道在给一个实例分配了多个分区的情况下,Spring Kafka如何处理重试。 Spring Kafka是否根据重试策略和退避策略继续重试同一条消息,还是重试?在重试之间,它是否从其他分区发送消息?

是这种行为吗?

A)重试消息->重试消息->重试消息

B)重试消息->其他消息->重试消息->重试消息

我看过其他的stackoverflow问题,这些问题似乎可以确认给定一个分区,Spring Kafka不会移动到另一个偏移量,但是如果有多个分区分配给实例,则没有有关行为的信息。我已经实现了具有重试模板和有状态重试的工厂。

@Bean
    public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>>
    kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<Integer, String> factory =
          new ConcurrentKafkaListenerContainerFactory<>();
        ListenerExceptions listenerExceptions = new ListenerExceptions();
        factory.setConsumerFactory(consumerFactory());
        factory.setConcurrency(KafkaProperties.CONCURRENCY);
        factory.getContainerProperties().setPollTimeout(KafkaProperties.POLL_TIMEOUT_VLAUE);
        factory.setRetryTemplate(retryTemplate());
        factory.setErrorHandler(new SeekToCurrentErrorHandler());
        factory.setStatefulRetry(true);
        factory.setRecoveryCallback((RetryContext context) -> listenerExceptions.recover(context));
        return factory;
    }

1 个答案:

答案 0 :(得分:1)

上述工厂的重试配置被委托到RetryingMessageListenerAdapter中,其逻辑如下:

public void onMessage(final ConsumerRecord<K, V> record, final Acknowledgment acknowledgment,
        final Consumer<?, ?> consumer) {
    RetryState retryState = null;
    if (this.stateful) {
        retryState = new DefaultRetryState(record.topic() + "-" + record.partition() + "-" + record.offset());
    }
    getRetryTemplate().execute(context -> {
                context.setAttribute(CONTEXT_RECORD, record);
                switch (RetryingMessageListenerAdapter.this.delegateType) {
                    case ACKNOWLEDGING_CONSUMER_AWARE:
                        context.setAttribute(CONTEXT_ACKNOWLEDGMENT, acknowledgment);
                        context.setAttribute(CONTEXT_CONSUMER, consumer);
                        RetryingMessageListenerAdapter.this.delegate.onMessage(record, acknowledgment, consumer);
                        break;
                    case ACKNOWLEDGING:
                        context.setAttribute(CONTEXT_ACKNOWLEDGMENT, acknowledgment);
                        RetryingMessageListenerAdapter.this.delegate.onMessage(record, acknowledgment);
                        break;
                    case CONSUMER_AWARE:
                        context.setAttribute(CONTEXT_CONSUMER, consumer);
                        RetryingMessageListenerAdapter.this.delegate.onMessage(record, consumer);
                        break;
                    case SIMPLE:
                        RetryingMessageListenerAdapter.this.delegate.onMessage(record);
                }
                return null;
            },
            getRecoveryCallback(), retryState);
}

因此,我们会根据邮件重试。根据Apache Kafka的建议,我们在一个线程中处理一个分区,因此在重试用完或调用成功之前,将不会处理该分区中的下一个记录。

根据您的多个分区条件和factory.setConcurrency(KafkaProperties.CONCURRENCY);配置,可能是在不同线程中处理了不同分区的事实。因此,可能存在同时重试来自不同分区的不同记录的情况。仅仅是因为重试与线程和调用堆栈相关。