[英]Dead letter queue (DLQ) for Kafka with spring-kafka
What is the best way to implement Dead letter queue (DLQ) concept in Spring Boot 2.0 application using spring-kafka 2.1.x to have all messages that were failed to be processed by @KafkaListener method of some bean sent to some predefined Kafka DLQ topic and not lose the single message? 使用spring-kafka 2.1.x在Spring Boot 2.0应用程序中实现死信队列(DLQ)概念的最佳方法是将某些bean的@KafkaListener方法无法处理的所有消息发送到某个预定义的Kafka DLQ主题而不是丢失单个消息?
So consumed Kafka record is either: 因此消耗的Kafka记录是:
I tried to create listener container with the custom implementation of the ErrorHandler sending records failed to be processed to DLQ topic using KafkaTemplate. 我尝试使用ErrorHandler的自定义实现创建侦听器容器,发送记录无法使用KafkaTemplate处理到DLQ主题。 Using disabled auto-commit and RECORD AckMode.
使用禁用的自动提交和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());
}
}
It seems that this implementation doesn't guarantee item #3 . 看来这个实现并不能保证第3项。 If an exception will be thrown in DlqErrorHandler record will not be consumed by the listener once again.
如果在DlqErrorHandler中抛出异常,则侦听器将不会再次使用该记录。
Will usage of the transactional listener container help? 使用事务监听器容器会有帮助吗?
factory.getContainerProperties().setTransactionManager(kafkaTransactionManager);
Is there any convenient way to implement DLQ concept using Spring Kafka? 有没有方便的方法来使用Spring Kafka实现DLQ概念?
UPDATE 28/03/2018 更新28/03/2018
Thanks to Gary Russell's answer I was able to achieve the desired behavior by implementing DlqErrorHandler as follows 感谢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);
}
}
}
This way if consumer poll returns 3 records (1, 2, 3) and the 2nd one can't be processed: 这样,如果消费者轮询返回3条记录(1,2,3),则无法处理第二条记录:
If sending to DLQ fails consumer seeks to the record.offset() and the record will be re-delivered to the listener (and sending to DLQ probably will be retired). 如果发送到DLQ失败,则消费者寻找record.offset()并且记录将被重新传递给监听器(并且发送到DLQ可能将被淘汰)。
See the SeekToCurrentErrorHandler
. 请参阅
SeekToCurrentErrorHandler
。
When an exception occurs, it seeks the consumer so that all unprocessed records are redelivered on the next poll. 发生异常时,它会搜索消费者,以便在下次轮询时重新传递所有未处理的记录。
You can use the same technique (eg a subclass) to write to the DLQ and seek the current offset (and other unprocessed) if the DLQ write fails, and seek just the remaining records if the DLQ write succeeds. 如果DLQ写入失败,您可以使用相同的技术(例如子类)写入DLQ并查找当前偏移量(以及其他未处理的偏移量),并在DLQ写入成功时查找剩余记录。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.