我来回阅读spring-kafka / kafka文档,但仍然找不到方法,如何通过错误恢复进行正确的事务处理。我相信这不是一个琐碎的问题,因此请仔细阅读直至结束。我相信整个问题都围绕着寻找方法,如何在失败的记录上重新定位或如何确认错误处理程序。但是,马比,还有更好的方法,我不知道。
因此记录正在流入,其中一些无效。我希望有一个最小的解决方案是(然后我将在其中修复您可能也看到的一些问题):
1)如果发生一些小事故,例如一或几条无效记录,我们不能停止生产。因此,如果kafka主题中的记录无效,我想将其记录或重新发送到其他队列,然后继续处理以下记录。
2)有永久性和临时性故障。永久失败是记录无法反序列化,记录失败的数据验证。在这种情况下,我想跳过无效记录,如1)中所述。临时故障可能是某些特定的异常或状态,例如数据库连接错误,网络问题等。在这种情况下,我们不想跳过失败的记录,而是希望在某些延迟后重试。
此问题的主题仅实现跳过/不跳过行为。
让我们说这是我们的出发点:
private Map<String, Object> createKafkaConsumerFactoryProperties(String bootstrapServers, String groupId, Class<?> valueDeserializerClass) {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, valueDeserializerClass);
props.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
return props;
}
@Bean(name="SomeFactory")
public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(
@Value("${…}") String bootstrapServers,
@Value("${…}") String groupId) {
ConcurrentKafkaListenerContainerFactory<String, String> factory =
new ConcurrentKafkaListenerContainerFactory<>();
ConsumerFactory<String, String> consumerFactory = new DefaultKafkaConsumerFactory<>(
createKafkaConsumerFactoryProperties(bootstrapServers, groupId, AvroDeserializer.class),
new StringDeserializer(),
new AvroDeserializer(SomeClass.class));
factory.setConsumerFactory(consumerFactory);
// factory.setConcurrency(2);
// factory.setBatchListener(true);
return factory;
}
我们有这样的听众:
@KafkaListener(topics = "${…}", containerFactory = "SomeFactory")
public void receive(@Valid List<SomeClass> messageList) {/*logic*/}
现在,如果我理解正确的话,该怎么做:
当侦听器获取消息时,〜当我们到达接收方法内部时,kafka消息将被确认,并且如果接收方法引发异常,则下一个轮询将返回以下记录。因为ack发生了,并且我们没有定义错误处理程序,所以记录错误处理程序将会启动。这不一定是我们想要的。我们可以使用SeekToCurrentErrorHandler重新处理消息。或者可以指定TransactionManager,并且如果异常从侦听器“泄漏”,则也会发生重新定位。如果有人知道这两种方法的性能比较,请告诉我。
当无法对消息进行反序列化时,反序列化器将失败,消息将不会被确认,并且将再次轮询相同的记录。这是某种“毒药包”,因为kafka会无限期旋转此消息。我们确实有retry.backoff.ms至少可以降低它的速度,但是我看不到任何最大数量的重试次数。因此,我们可以做的最好的事情就是在这种情况下停止/暂停容器。这是苛刻的方法。顺便说一句。我是kafka / spring-kafka的新手,我从没看到任何地方提及过,如何从应用程序外部手动重新定位偏移,这意味着确定,侦听器已关闭,但是现在呢?另一个解决方案是不使反序列化器失败,并返回一些东西。但是呢? KafkaNull,很好,但是我们的监听器将失败,因为SomeClass ClassCastException。我们可以发送一些SomeClass的人为值,这又是可怕的,因为这不是我们实际获得的数据。这在架构上也是不正确的。
或者我们可以使用重新定位错误处理程序,如果我们知道该怎么做的话,那会很棒。我需要寻求下一个记录。但是,尽管文档说,ErrorHandler应该传达导致失败的记录,但似乎失败了。因此,即使在非批处理侦听器中,我也有记录列表(1个失败+一堆未处理的记录),也不知道将偏移量设置在何处。
那么这种疯狂的解决方法是什么? 好吧,我现在能想到的最好的办法是很难看的:不要在反序列化器中失败(坏),不要在侦听器中接受特定类型(坏),手动过滤掉KafkaNulls(坏),最后手动触发bean验证(坏) 。有没有更好的办法?感谢您的大力支持,对于实现此目标的所有提示或指导,我将不胜感激。
答案 0 :(得分:0)
请参见the documentation for the upcoming 2.2 release (due tomorrow)。
DefaultAfterRollbackProcessor
(在使用事务时)和SeekToCurrentErrorHandler
(在不使用事务时)现在可以恢复(跳过)保持失败的记录,并且默认情况下会失败10次。可以将它们配置为将失败的记录发布到死信主题。
另请参见Error Handling Deserializer,它捕获反序列化问题并将其传递到容器,以便可以将它们发送到错误处理程序。