![](/img/trans.png)
[英]spring-kafka: RetryTopicConfiguration with custom names for retry and dead letter topics
[英]Dead letter queue (DLQ) for Kafka with spring-kafka
使用spring-kafka 2.1.x在Spring Boot 2.0應用程序中實現死信隊列(DLQ)概念的最佳方法是將某些bean的@KafkaListener方法無法處理的所有消息發送到某個預定義的Kafka DLQ主題而不是丟失單個消息?
因此消耗的Kafka記錄是:
我嘗試使用ErrorHandler的自定義實現創建偵聽器容器,發送記錄無法使用KafkaTemplate處理到DLQ主題。 使用禁用的自動提交和RECORD AckMode。
spring.kafka.enable-auto-ack=false
spring.kafka.listener.ack-mode=RECORD
@Configuration
public class KafkaConfig {
@Bean
ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = ...
...
factory.getContainerProperties().setErrorHandler(dlqErrorHandler);
return factory;
}
}
@Component
public class DlqErrorHandler implements ErrorHandler {
@Autowired
private KafkaTemplate<Object, Object> kafkaTemplate;
@Value("${dlqTopic}")
private String dlqTopic;
@Override
public void handle(Exception thrownException, ConsumerRecord<?, ?> record) {
log.error("Error, sending to DLQ...");
kafkaTemplate.send(dlqTopic, record.key(), record.value());
}
}
看來這個實現並不能保證第3項。 如果在DlqErrorHandler中拋出異常,則偵聽器將不會再次使用該記錄。
使用事務監聽器容器會有幫助嗎?
factory.getContainerProperties().setTransactionManager(kafkaTransactionManager);
有沒有方便的方法來使用Spring Kafka實現DLQ概念?
更新28/03/2018
感謝Gary Russell的回答,我能夠通過實現DlqErrorHandler來實現所需的行為,如下所示
@Configuration
public class KafkaConfig {
@Bean
ConcurrentKafkaListenerContainerFactory<Integer, String> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Integer, String> factory = ...
...
factory.getContainerProperties().setAckOnError(false);
factory.getContainerProperties().setErrorHandler(dlqErrorHandler);
return factory;
}
}
@Component
public class DlqErrorHandler implements ContainerAwareErrorHandler {
...
@Override
public void handle(Exception thrownException, list<ConsumerRecord<?, ?> records, Consumer<?, ?> consumer, MessageListenerContainer container) {
Consumerrecord<?, ? record = records.get(0);
try {
kafkaTemplate.send("dlqTopic", record.key, record.value());
consumer.seek(new TopicPartition(record.topic(), record.partition()), record.offset() + 1);
// Other records may be from other partitions, so seek to current offset for other partitions too
// ...
} catch (Exception e) {
consumer.seek(new TopicPartition(record.topic(), record.partition()), record.offset());
// Other records may be from other partitions, so seek to current offset for other partitions too
// ...
throw new KafkaException("Seek to current after exception", thrownException);
}
}
}
這樣,如果消費者輪詢返回3條記錄(1,2,3),則無法處理第二條記錄:
如果發送到DLQ失敗,則消費者尋找record.offset()並且記錄將被重新傳遞給監聽器(並且發送到DLQ可能將被淘汰)。
請參閱SeekToCurrentErrorHandler
。
發生異常時,它會搜索消費者,以便在下次輪詢時重新傳遞所有未處理的記錄。
如果DLQ寫入失敗,您可以使用相同的技術(例如子類)寫入DLQ並查找當前偏移量(以及其他未處理的偏移量),並在DLQ寫入成功時查找剩余記錄。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.