Skip to content
Kafka Is FunProgress

Production Patterns · Intermediate

@RetryableTopic (Non-Blocking Retry)

Non-blocking retry is superior to blocking retry. Failed messages go to retry topics; the main consumer continues processing without pausing. Strongly preferred for production.

OrderConsumer.java (with @RetryableTopic)java
@Service
@Slf4j
public class OrderConsumer {

    @RetryableTopic(
        attempts = "4",           // 1 original + 3 retries
        backoff = @Backoff(
            delay = 1_000,        // 1 second initial delay
            multiplier = 2,       // Double each retry: 1s, 2s, 4s
            maxDelay = 10_000     // Cap at 10 seconds
        ),
        dltTopicSuffix = "-dlt",
        // auto-creates: orders.order.created-retry-0, -retry-1, -retry-2, -dlt
        include = {TransientServiceException.class},
        exclude = {InvalidEventException.class}  // Go directly to DLT
    )
    @KafkaListener(topics = "orders.order.created", groupId = "order-processor")
    public void onOrderCreated(OrderEvent event, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
        log.info("Processing order from topic: {}", topic);
        orderService.processOrder(event); // Throws → triggers retry
    }

    // Dedicated DLT handler
    @DltHandler
    public void handleDlt(OrderEvent event, @Header(KafkaHeaders.RECEIVED_TOPIC) String topic) {
        log.error("DLT received from topic {}: orderId={}", topic, event.getOrderId());
        // Alert, store for manual review, or metrics counter
        metricsService.incrementDltCounter(topic);
    }
}