问题现象
发布需求代码,一天后发现redis内存激增告警,回滚代码后,问题仍然没有解决。
问题分析处理过程
1.导出reidsKey,分析增长数据
发现是以 session_key_*** 开头的 key 激增,分析应该是网关,且激增时间开始节点,是网关需求发版时间
2.监控线上日志
发现有频繁反序列化异常
3.查看类源代码及git修改记录
发现反序列化异常的类的父类 实现了 【java.io.Serializable】,但该类没有定义 serialVersionUID
4.查看反序列化生成对象
了解 session 对象是存redis,然后反序列化构造,生成新session后,因为代码逻辑原因,并没有替换新session,一直使用老session,所以导致错误数据用户一直反复登录。
5.结论
1.如果没有自定义 serialVersionUID, JDK会根据类属性类型,生成 serialVersionUID,当类属性有更改,serialVersionUID将会重新生成。
问题解决方案
1.方案1
自定义serialVersionUID,且serialVersionUID定义为 stream 流中的 serialVersionUID,但代码回滚后又生成多种serialVersionUID的数据,无法完全覆盖历史数据,只能兼容部分报错数据。
2.方案2
删除所有redis数据,用户没有seesion,全都重新登录替换。缺点:影响用户使用,体验
3.方案3(采用)
重写ObjectInputstream,读取stream中的serialVersionUID 和 local Class 的 serialVersionUID 做对比,如果不同,强制将 local Class 的serialVersionUID写入stream,保证serialVersionUID一致,数据反序列化兼容
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
/**
* 原代码:
* ObjectInputStream objectInputStream = new ObjectInputStream(byteStream);
*/
ObjectInputStream objectInputStream = new CompatibleInputStream(byteStream);
AccountInfo result = objectInputStream.readObject();