记一次Serializable序列化和反序列化导致的线上报错
背景
某个需求上线后告警群里面收到了报错的消息,报错摘要如下:
Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: com.i61.draw.common.model.CommonTeacherInfo; local class incompatible: stream classdesc serialVersionUID = -5929137330017767311, local class serialVersionUID = -2020770249958646674 org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.InvalidClassException: com.xxx.xxx.common.model.CommonXxxxxxInfo; local class incompatible: stream classdesc serialVersionUID = -5929137330017767311, local class serialVersionUID = -2020770249958646674
排查
通过报错的栈信息,定位到问题代码第66行,可以看到是调getTeacherInfoList这个方法时,读取缓存反序列化失败了。
将报错信息复制到GPT,GPT的回答如下:
这个错误表明在反序列化过程中发生了问题,具体原因是由于 CommonXxxxxxInfo
类的版本不一致导致的。在Java中,当一个类被序列化时,会生成一个包含版本信息的serialVersionUID。如果在反序列化时,类的版本信息不匹配,就会抛出 InvalidClassException
。
在你的具体情况中,错误信息中提到了两个不同的serialVersionUID值:
- stream classdesc serialVersionUID = -5929137330017767311
- local class serialVersionUID = -2020770249958646674
这表示你的代码中的 CommonXxxxxxInfo
类在两个地方的版本不一致。可能的原因和解决方法包括:
-
类的版本变更: 可能是
CommonXxxxxxInfo
类在序列化和反序列化之间发生了更改,可能是添加、删除或修改了字段。确保在序列化和反序列化的代码之间,CommonXxxxxxInfo类的版本是一致的。 -
显式指定serialVersionUID: 你可以在
CommonXxxxxxInfo
类中显式指定serialVersionUID,以确保它在类发生变更时仍然保持一致。你可以在类中添加如下代码:javaCopy code
private static final long serialVersionUID = -5929137330017767311L; // 使用实际的值替换
在类发生变更时,手动更新这个值。
-
检查依赖版本: 如果
CommonXxxxxxInfo
类是从外部库引入的,确保你的应用程序和库中使用的类版本一致。可能你的应用程序使用了一个不同版本的CommonXxxxxxInfo
类。
通过解决这些问题,你应该能够解决反序列化问题。在做任何更改之前,请确保备份你的代码以防意外。
其中有提到,由于没有指定serialVersionUID,当添加、删除或修改了字段,其默认的serialVersionUID会变更,查看CommonXxxxxxInfo类的git提交记录,果然添加了字段。
解决
- 手动生成serialVersionUID值
总结
以前看到说继承了Serializable最好指定serialVersionUID,但在实际使用中,发现似乎不指定也没有问题,所以即使继承了这个接口也很少会再手动指定UID,所以以后遇到需要序列化(尤其是使用jdk序列化器)的地方,都应该:
- 需要序列化的地方(尤其是使用jdk序列化起)都要继承Serializable接口
- 手动生成serialVersionUID值
- 如果没有指定UID,当类结构发生变化(增减改字段)时,默认的UID会变化,导致序列化/反序列化失败
小提示:生成serialVersionUID值时可以使用IDEA插件GenerateSerialVersionUID
- 感谢你赐予我前进的力量