繁体   English   中英

处理 Kafka 流中的异常

[英]Handling exceptions in Kafka streams

已经浏览了多个帖子,但其中大部分都与处理坏消息有关,而不是处理它们时的异常处理。

我想知道如何处理流应用程序收到的消息并且在处理消息时出现异常? 异常可能是由于多种原因造成的,例如网络故障、运行时异常等,

  • 有人可以建议什么是正确的做法吗? 我应该使用setUncaughtExceptionHandler吗? 或者,还有更好的方法?
  • 如何处理重试?

这取决于您想对生产者方面的异常做什么。 如果生产者将抛出异常(例如,由于网络故障或 kafka 代理已死亡),则默认情况下流将死亡。 使用 kafka-streams 1.1.0 版,您可以通过实现ProductionExceptionHandler来覆盖默认行为,如下所示:

public class CustomProductionExceptionHandler implements ProductionExceptionHandler {

    @Override
    public ProductionExceptionHandlerResponse handle(final ProducerRecord<byte[], byte[]> record,
                                                     final Exception exception) {
        log.error("Kafka message marked as processed although it failed. Message: [{}], destination topic: [{}]",  new String(record.value()), record.topic(), exception);
        return ProductionExceptionHandlerResponse.CONTINUE;
    }

    @Override
    public void configure(final Map<String, ?> configs) {
    }

}

从 handle 方法中,如果您不希望流因异常而死亡,您可以返回CONTINUE如果您希望流停止(FAIL 是默认值),则返回FAIL 并且您需要在流配置中指定此类:

default.production.exception.handler=com.example.CustomProductionExceptionHandler

还要注意ProductionExceptionHandler只处理生产者上的异常,它不会处理使用流方法mapValues(..) , filter(..) , branch(..)等处理消息时的异常,你需要用try / catch 块(将所有方法逻辑放入 try 块中以保证您将处理所有异常情况):

.filter((key, value) -> { try {..} catch (Exception e) {..} })

据我所知,我们不需要在消费者端显式处理异常,因为 kafka 流将在稍后自动重试消费(因为在消息被消费和处理之前不会更改偏移量); 例如,如果 kafka 代理在一段时间内无法访问,您将收到来自 kafka 流的异常,并且当损坏时,kafka 流将消耗所有消息。 所以在这种情况下,我们只会延迟,没有任何损坏/丢失。

使用setUncaughtExceptionHandler您将无法像使用ProductionExceptionHandler一样更改默认行为,使用它您只能记录错误或将消息发送到故障主题。


kafka-streams 2.8.0以来的更新

kafka-streams 2.8.0 ,您可以使用KafkaStreams方法自动替换失败的流线程(由未捕获的异常引起) void setUncaughtExceptionHandler(StreamsUncaughtExceptionHandler eh); StreamThreadExceptionResponse.REPLACE_THREAD 有关更多详细信息,请查看Kafka Streams Specific Uncaught Exception Handler

kafkaStreams.setUncaughtExceptionHandler(ex -> {
    log.error("Kafka-Streams uncaught exception occurred. Stream will be replaced with new thread", ex);
    return StreamsUncaughtExceptionHandler.StreamThreadExceptionResponse.REPLACE_THREAD;
});

为了处理消费者端的异常,

1)您可以使用以下属性在生产者中添加默认异常处理程序。

"default.deserialization.exception.handler" = "org.apache.kafka.streams.errors.LogAndContinueExceptionHandler";

基本上 apache 提供了三个异常处理程序类作为

1) LogAndContiuneExceptionHandler 你可以把它当作

props.put(StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG, 
           LogAndContinueExceptionHandler.class);

2) LogAndFailExceptionHandler

props.put(StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG, 
           LogAndFailExceptionHandler.class);

3) LogAndSkipOnInvalidTimestamp

props.put(StreamsConfig.DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG, 
           LogAndSkipOnInvalidTimestamp.class);

对于自定义异常处理,

1)您可以实现 DeserializationExceptionHandler 接口并覆盖 handle() 方法。

2)或者您可以扩展上述类。

setUncaughtExceptionHandler无助于处理异常,它在流因某些未捕获的异常而终止后起作用。

Kafka 提供了几种处理异常的方法。 一个简单的try-catch{}将有助于捕获处理器代码中的异常,但 kafka 反序列化异常(可能是由于数据问题)和生产异常(在与代理通信期间发生)分别需要DeserializationExceptionHandlerProductionExceptionHandler 默认情况下,如果遇到任何这些情况,kafka 应用程序将失败。

你可以在这个帖子里找到

在 Spring cloud stream 中,您可以使用以下方法配置自定义反序列化处理程序:

  • spring.cloud.stream.kafka.streams.binder.configuration.default.deserialization.exception.handler =your-package-name.CustomLogAndContinueExceptionHandler

  • CustomLogAndContinueExceptionHandler扩展 LogAndContinueExceptionHandler 或实现 DeserializationExceptionHandler

  • CustomLogAndContinueExceptionHandler DeserializationHandlerResponse.CONTINUE 或 FAIL取决于您的用例

@Slf4j
public class CustomLogAndContinueExceptionHandler extends LogAndContinueExceptionHandler {

    @Override
    public DeserializationHandlerResponse handle(ProcessorContext context, ConsumerRecord<byte[], byte[]> record,
            Exception exception) {
.... some business logic here ....
        log.error("Message failed: taskId: {}, topic: {}, partition: {}, offset: {}, , detailerror : {}",
                context.taskId(), record.topic(), record.partition(), record.offset(), exception.getMessage());
        return DeserializationHandlerResponse.CONTINUE;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM