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.
@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);
}
}