Continue consuming subsequent records in reactor kafka after deserialization exception

半腔热情 提交于 2021-01-28 19:50:12

问题


I am using reactor kafka and have a custom AvroDeserializer class for deserialization of messages.

Now I have a case where for certain payloads the deserialization class throws an exception. My Kafka listener dies as soon as it tries to read such records. I tried handling this exception using onErrorReturn and using combination of (doOnError and onErrorContinue), however, it helped log the exception, but failed to consume subsequent records.

public class AvroDeserializer<T extends SpecificRecordBase> implements Deserializer<T> {
   public T deserialize( String topic, byte[] data ) {
       try {
         //logic to deserialize
       }
       catch {
          // throw new SerializationException("error deserializing" );
       }
   }
}

At the listener end, I'm trying to handle like this ->

@EventListener(ApplicationStartedEvent.class)

public class listener() {
   KakfaReceiver<String, Object> receiver; // kafka listener
   receiver.receive()
   .delayUntil(do something)//logic to update the record to db
   .doOnError(handle error)//trying to handle the exceptions including desrialization exception - logging them to a system
   .onErrorContinue((throwable, o) -> log.info("continuing"))
   .doOnNext(r->r.receiverOffset().acknowledge())
   .subscribe()

}

One option is not to throw exception from the Deserializer class, but I want to log such exceptions in a separate system for analytics, and hence want handling of such records at the kafka listener end. Any thoughts on how this can be achieved?


回答1:


Looks like the suggestion in the comment about using

spring.cloud.stream.kafka.streams.binder.deserializationExceptionHandler=logAndContinue

will work in most cases. But I couldnt figure out of the way to make it work in Reactor Spring Kafka. For now, I went ahead with the approach of not throwing an exception from the deserializer and adding the logic to log it there itself,and that solves the issue of Kafka consumer not being able to consume subsequent records after that on poison record




回答2:


I think your problem is that you do throw an exception and any exception is a terminal even in context of a reactive stream. Just don't throw an exception.

Wrap your T (that you return from deserialize()) in a class, that can indicate whether deserialization was a success or failure. If you don't care for exact error you can use Optional<T> where empty optional will indicate deserialization error. If you are using vavr library, you can grab Option<T> from there or Try<T> to store the actual deserialization on the Failure side.

For example, your deserializer would look like

public class AvroDeserializer<T extends SpecificRecordBase> implements Deserializer<T> {
   public Optional<T> deserialize( String topic, byte[] data ) {
       try {
         //logic to deserialize
         return Optional.of(result);
       }
       catch {
          return Optional.empty();
       }
   }
}

Now that you are no longer throwing, your flux stays alive and you can for example filter out empty Optional instances.

P.S. If you don't want to use wrapper types, you can return null instead of throwing and filter out null values. I wound not recommend that though - easier to forget about that deserialize() behavior and get NullPointerException later.



来源:https://stackoverflow.com/questions/62961279/continue-consuming-subsequent-records-in-reactor-kafka-after-deserialization-exc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!