Kafka在消费者反序列化时出现问题
问题描述
今天在启动Kafka时,出现了一些问题。Kafka启动后,卡在了某一消费点,报
Missing exception handling for deserialization of key values,提示缺少对键值反序列的异常处理,并且系统一直重复反序列化该调记录,一直失败,陷入死循环。
上网查询解决办法发现这种现象是当生产者序列化程序和消费者反序列化程序不兼容时产生的一种毒丸场景。在以下场景可能会发生毒丸现象:
-
生产者更改了键或值序列化器,并继续将数据生产到同一Kafka主题。这给该主题的所有消费者带来了反序列化问题。
-
使用者配置了错误的key或值反序列化器。
-
不同的生产者使用不同的键或值序列化程序,开始生产有关Kafka主题的记录。
在发生这种情况后如果不处理将会产生很大的影响:
-
消费者应用程序正在使用Kafka主题。
-
在某个时间点,应用程序无法对记录进行反序列化(遇到毒丸)。
-
消费者不能处理毒丸。
-
因为使用者偏移量没有向前移动,所以阻止了主题分区的使用。
-
消费者将一次又一次地(非常迅速地)尝试反序列化记录,但是永远不会成功。因此,您的消费者应用程序将陷入一个无穷循环,尝试对失败的记录进行反序列化。
-
对于每次失败,都会在您的日志文件中写入一行。
解决过程
Kafka提供了ErrorHandlingDeserializer用来解决上述问题。
在配置文件中配置
spring.kafka.consumer.key-deserializer=org.springframework.kafka.support.serializer.ErrorHandlingDeserializer spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.ErrorHandlingDeserializer
(当反序列化失败时,.ErrorHandlingDeserializer会去调用ErrorHander去捕获异常,并把异常信息打印在控制台上)
以及
spring.kafka.consumer.properties.spring.deserializer.key.delegate.class=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.properties.spring.deserializer.value.delegate.class=org.springframework.kafka.support.serializer.JsonDeserializer(消费者实际的反序列化配置)
配置了ErrorHandlingDeserializer后,系统将不会阻塞在反序列化异常的消费点上,而是将异常捕获打印后,继续运行程序。Kafka还提供了
ErrorHandlingDeserializer与DeadLetterPublishingRecoverer,SeekToCurrentErrorHandler组合使用将死信记录发布到死信主题上进行保存,方便后期排查问题。
在配置了ErrorHandlingDeserializer后,再次启动Kafka,发现问题变成了is not in the trusted packages: If you believe this class is safe to deserialize, please provide its name. If the serialization is only done by a trusted source, you can also enable trust all (*).提示要被反序列化的消息类不在被信任的包内。这个问题其实是因为自定义消息在默认情况下是被Kafka信任的,解决办法是在配置文件中添加需要被信任包的路径:spring.kafka.consumer.properties.spring.json.trusted.packages=xxx。其实这个问题以前就已经处理了,在一开始还没想明白为什么还会出这个问题,我就把信任路径直接设置为**,表示信任全部路径下的包。
再次运行后发现报错信息又变了,改为failed to deserialize; nested exception is org.springframework.messaging.converter.MessageConversionException: failed to resolve class name. Class not found
找不到要加载的类,仔细观察后,发现提示找不到该路径下对应的类。
然后突然想起来公司最近对这个Kafka进行了一次重构操作,新老版本没有去区分开,而是共用了一个Kafka,然而里面的消息类的路径又发生了一些改变,所以在运行新版本项目里的Kafka时,读取到了一些旧版本项目里的Kafka消息,消费者无法对消息进行正常的反序列化而导致的异常。找到问题后,进行处理,系统便能正常运行了。
问题总结
项目在运行时总会遇到一些奇怪的问题,而避免这些问题的最好方法应该是合理规范的开发,例如这次的问题就出在系统重构时,没有将两个项目的Kafka独立使用,而是共用一个Kafka导致出现问题。所以在开发中,解耦还是非常重要的,做到尽量不要将关联性不大的东西耦合在一起。