在Kafka流中处理异常

时间:2018-07-12 07:21:26

标签: java apache-kafka-streams spring-kafka

已经经历了多个帖子,但是大多数都是相关的处理错误消息,而不是关于处理它们时的异常处理。

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

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

提前谢谢!

4 个答案:

答案 0 :(得分:5)

这取决于您要如何处理生产者方面的异常。 如果将对生产者抛出异常(例如,由于网络故障或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) {
    }

}
从句柄方法

,如果您不希望流因异常而死,则可以返回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那样更改默认行为,因为它只能记录错误或将消息发送到失败主题。

答案 1 :(得分:2)

要在消费者端处理异常,

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)或者您可以扩展上述类。

答案 2 :(得分:1)

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

Kafka提供了几种处理异常的方法。一个简单的 try-catch {} 将有助于捕获处理器代码中的异常,但是kafka反序列化异常(可能是由于数据问题引起的)和生产异常(与代理进行通信时发生)需要 DeserializationExceptionHandler < / strong>和 ProductionExceptionHandler 。默认情况下,如果遇到任何上述情况,kafka应用程序都会失败。

您可以在此post

上找到

答案 3 :(得分:0)

在 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;
    }
}