Apache Ignite对象反序列化失败问题求助
Tomcat + Ignite 运行一段时间后出现 SequenceEditableList 反序列化失败(filter status: REJECTED)
环境
- Tomcat 8
- Ignite 2.11.1
- JDK 11 (Corretto)
问题描述
在Tomcat 8上运行一段时间后,使用Ignite存储HTTP会话的Web应用持续触发同一类型的反序列化错误,但应用启动初期运行正常。
SequenceEditableList 类继承自 ArrayList<>
错误日志
Caused by: org.apache.ignite.IgniteCheckedException: Failed to deserialize object with given class loader: [clsLdr=ParallelWebappClassLoader context: xxxxxx delegate: false ----------> Parent Classloader: java.net.URLClassLoader@4277127c , err=Failed to deserialize object [typeName=com.xxxx.web.common.list.SequenceEditableList]] at org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller.unmarshal0(OptimizedMarshaller.java:261) at org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller.unmarshal0(OptimizedMarshaller.java:225) at org.apache.ignite.marshaller.AbstractNodeNameAwareMarshaller.unmarshal(AbstractNodeNameAwareMarshaller.java:92) at org.apache.ignite.internal.binary.BinaryUtils.doReadOptimized(BinaryUtils.java:1816) ... 137 common frames omitted Caused by: java.io.IOException: Failed to deserialize object [typeName=com.xxxx.web.common.list.SequenceEditableList] at org.apache.ignite.internal.marshaller.optimized.OptimizedObjectInputStream.readObject0(OptimizedObjectInputStream.java:357) at org.apache.ignite.internal.marshaller.optimized.OptimizedObjectInputStream.readObjectOverride(OptimizedObjectInputStream.java:205) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:484) at java.base/java.io.ObjectInputStream.readObject(ObjectInputStream.java:451) at org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller.unmarshal0(OptimizedMarshaller.java:251) ... 140 common frames omitted Caused by: java.io.IOException: java.lang.reflect.InvocationTargetException at org.apache.ignite.internal.marshaller.optimized.OptimizedObjectInputStream.readSerializable(OptimizedObjectInputStream.java:611) at org.apache.ignite.internal.marshaller.optimized.OptimizedClassDescriptor.read(OptimizedClassDescriptor.java:985) at org.apache.ignite.internal.marshaller.optimized.OptimizedObjectInputStream.readObject0(OptimizedObjectInputStream.java:353) ... 144 common frames omitted Caused by: java.lang.reflect.InvocationTargetException: null at java.base/jdk.internal.reflect.GeneratedMethodAccessor169.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.apache.ignite.internal.marshaller.optimized.OptimizedObjectInputStream.readSerializable(OptimizedObjectInputStream.java:608) ... 146 common frames omitted Caused by: java.io.InvalidClassException: filter status: REJECTED at java.base/java.io.ObjectInputStream.filterCheck(ObjectInputStream.java:1356) at java.base/java.io.ObjectInputStream.checkArray(ObjectInputStream.java:1386) at java.base/java.util.ArrayList.readObject(ArrayList.java:924) ... 150 common frames omitted
问题根因
错误栈最底层的java.io.InvalidClassException: filter status: REJECTED是核心原因,这是JDK 9+引入的**序列化过滤机制(JEP 290)**触发的拒绝行为。结合场景分析:
- Tomcat Web应用的类加载器(ParallelWebappClassLoader)在运行过程中可能发生重新加载(比如热部署),导致Ignite中存储的旧会话对象对应的类版本与当前类加载器中的类不匹配,被序列化过滤器拒绝。
- Ignite的OptimizedMarshaller没有正确关联Web应用的类加载器,导致反序列化时使用的类加载器上下文不符合过滤器规则。
- JDK默认的序列化过滤规则限制了ArrayList子类的反序列化操作。
解决方案
1. 调整JDK序列化过滤器规则
通过JVM参数明确允许目标类的反序列化,根据安全需求选择合适的规则:
- 允许指定类及依赖类:
-Djdk.serialFilter=com.xxxx.web.common.list.SequenceEditableList;java.util.ArrayList;*
- 允许Web应用全量包及基础JDK类:
-Djdk.serialFilter=com.xxxx.**;java.util.**;java.lang.**
2. 配置Ignite使用Web应用类加载器
在Ignite初始化时,指定使用Web应用的上下文类加载器,确保序列化/反序列化时类加载器一致:
IgniteConfiguration cfg = new IgniteConfiguration(); OptimizedMarshaller marshaller = new OptimizedMarshaller(); // 使用当前Web应用的类加载器 marshaller.setClassLoader(Thread.currentThread().getContextClassLoader()); // 开启类加载器缓存,避免频繁创建 marshaller.setClassLoaderCacheEnabled(true); cfg.setMarshaller(marshaller); Ignite ignite = Ignition.start(cfg);
如果使用XML配置:
<bean class="org.apache.ignite.configuration.IgniteConfiguration"> <property name="marshaller"> <bean class="org.apache.ignite.internal.marshaller.optimized.OptimizedMarshaller"> <property name="classLoader" value="#{T(Thread).currentThread().getContextClassLoader()}"/> <property name="classLoaderCacheEnabled" value="true"/> </bean> </property> <!-- 其他Ignite配置 --> </bean>
3. 避免类加载器泄漏与热部署影响
如果应用存在热部署场景,确保Web应用停止时正确关闭Ignite实例,避免旧类加载器被持有:
public class IgniteSessionListener implements ServletContextListener { private Ignite igniteInstance; @Override public void contextInitialized(ServletContextEvent event) { // 初始化Ignite IgniteConfiguration cfg = new IgniteConfiguration(); // ...配置项 igniteInstance = Ignition.start(cfg); } @Override public void contextDestroyed(ServletContextEvent event) { if (igniteInstance != null && !igniteInstance.isClosed()) { igniteInstance.close(); } } }
同时,建议在生产环境禁用Tomcat热部署,减少类加载器变更带来的问题。
4. 自定义序列化逻辑
为SequenceEditableList添加自定义序列化方法,绕过ArrayList默认的序列化流程,避免触发过滤器检查:
public class SequenceEditableList<T> extends ArrayList<T> { // 原有业务代码... private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); // 手动写入集合大小和元素 out.writeInt(this.size()); for (T element : this) { out.writeObject(element); } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); int size = in.readInt(); this.ensureCapacity(size); // 手动读取元素 for (int i = 0; i < size; i++) { this.add((T) in.readObject()); } } }
内容的提问来源于stack exchange,提问作者Mr_Thorynque
相关产品推荐
相关产品推荐

