Блокирующий повтор:
@Bean
DefaultErrorHandler errorHandler(KafkaTemplate<Object, Object> t) {
var h = new DefaultErrorHandler(new DeadLetterPublishingRecoverer(t),
new ExponentialBackOff(1000, 2.0) {{ setMaxElapsedTime(30_000); }});
h.addNotRetryableExceptions(ValidationException.class);
return h;
}
Запись повторяется на месте; пока идут повторы, остальные записи партиции ждут — порядок сохраняется, но долгая отсрочка задерживает поток. Повторы держат потребителя — нельзя ставить отсрочку длиннее max.poll.interval.ms.
Неблокирующий повтор:
@RetryableTopic(attempts = "4", backoff = @Backoff(delay = 1000, multiplier = 10),
exclude = ValidationException.class)
@KafkaListener(topics = "payments")
void handle(PaymentEvent e) { ... }
Spring создаёт топики payments-retry-0, -retry-1, … и payments-dlt. Упавшая запись публикуется в retry-топик и будет прочитана после задержки, а основная партиция обрабатывается дальше. Цена — потеря порядка для этого ключа и больше топиков.
Что выбрать: порядок важен (события одной сущности) — блокирующий с небольшим числом попыток; порядок не важен, важна пропускная способность — неблокирующий. В обоих случаях: неповторяемые исключения — сразу в DLT, десериализация — через ErrorHandlingDeserializer, мониторинг DLT.