Hazelcast类在EntryExpiredListener上没有发现异常



当使用EntryExpiredListener时,由于类未找到,我正在获得序列化异常。

我创建了一个测试罐并缩小了问题范围。

Test jar包含TestService, CacheKey和CacheValue.TestService将键类型为CacheKey,值类型为CacheValue插入到IMap中。它注册一个EntryExpiryListener来侦听插入条目的过期。

TestService的代码很简单:
hazelcastService.put(
new CacheKey("11", "12345",  "678910", 1624174140000L),
new CacheValue(new HashMap<>())); // ttl is set to 30 sec
Collection<HazelcastInstance> instances = HazelcastClient.getAllHazelcastClients();
Optional<HazelcastInstance> instance = instances.stream().findAny();
if (!instance.isPresent()) {
throw new IllegalStateException("Can not find Hazelcast instance!");
}
// HazelcastInstance is created by framework component
IMap<CacheKey, CacheValue> cacheMap = instance.get().getMap(cacheName);
cacheMap.addEntryListener((EntryExpiredListener<CacheKey, CacheValue>) event -> {
CacheKey key = event.getKey();
CacheValue val = event.getOldValue();
}, true);
public class CacheKey implements Serializable {
private static final long serialVersionUID = 123412453256L;
private final String type;
private final String field1;
private final String field2;
private final long timestamp;

// constructor, getters
public class CacheValue implements Serializable {
private static final long serialVersionUID = 658933453256L;
private final HashMap<String, Object> recordMap;

// constructor, getters

当条目过期时,由于类未发现CacheKey异常而抛出序列化异常。

Method threw 'com.hazelcast.nio.serialization.HazelcastSerializationException' exception. Cannot evaluate com.hazelcast.map.impl.DataAwareEntryEvent.toString()
2021-10-19 16:41:13,921 ERROR [hz.client_1.event-4] c.h.c.impl.spi.ClientListenerService hz.client_1 [nifi] [4.2] hz.client_1.event-4 caught an exception while processing:com.hazelcast.client.impl.spi.impl.listener.ClientListenerServiceImpl$ClientEventProcessor@782e8844
com.hazelcast.nio.serialization.HazelcastSerializationException: java.lang.ClassNotFoundException: com.myapp.expirytest.CacheKey
at com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:90)
at com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:79)
at com.hazelcast.internal.serialization.impl.StreamSerializerAdapter.read(StreamSerializerAdapter.java:44)
at com.hazelcast.internal.serialization.impl.AbstractSerializationService.toObject(AbstractSerializationService.java:208)
at com.hazelcast.map.impl.DataAwareEntryEvent.getKey(DataAwareEntryEvent.java:74)
at com.ipfli.drs.nifi.processor.hzexpirytest.ExpiryTest.lambda$onScheduled$0(ExpiryTest.java:112)
at com.hazelcast.map.impl.MapListenerAdaptors.lambda$null$6(MapListenerAdaptors.java:93)
at com.hazelcast.map.impl.InternalMapListenerAdapter.onEvent(InternalMapListenerAdapter.java:56)
at com.hazelcast.map.impl.InternalMapListenerAdapter.onEvent(InternalMapListenerAdapter.java:35)
at com.hazelcast.client.impl.proxy.ClientMapProxy$AbstractClientMapEventHandler.handleEntryEvent(ClientMapProxy.java:2004)
at com.hazelcast.client.impl.proxy.ClientMapProxy$ClientMapEventHandler$1.handleEntryEvent(ClientMapProxy.java:1981)
at com.hazelcast.client.impl.protocol.codec.MapAddEntryListenerCodec$AbstractEventHandler.handle(MapAddEntryListenerCodec.java:164)
at com.hazelcast.client.impl.proxy.ClientMapProxy$ClientMapEventHandler.handle(ClientMapProxy.java:1989)
at com.hazelcast.client.impl.proxy.ClientMapProxy$ClientMapEventHandler.handle(ClientMapProxy.java:1971)
at com.hazelcast.client.impl.spi.impl.listener.ClientListenerServiceImpl.handleEventMessageOnCallingThread(ClientListenerServiceImpl.java:189)
at com.hazelcast.client.impl.spi.impl.listener.ClientListenerServiceImpl$ClientEventProcessor.run(ClientListenerServiceImpl.java:356)
at com.hazelcast.internal.util.executor.StripedExecutor$Worker.process(StripedExecutor.java:245)
at com.hazelcast.internal.util.executor.StripedExecutor$Worker.run(StripedExecutor.java:228)
Caused by: java.lang.ClassNotFoundException: com.myapp.expirytest.CacheKey
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
at com.hazelcast.internal.nio.ClassLoaderUtil.tryLoadClass(ClassLoaderUtil.java:289)
at com.hazelcast.internal.nio.ClassLoaderUtil.loadClass(ClassLoaderUtil.java:249)
at com.hazelcast.internal.nio.IOUtil$ClassLoaderAwareObjectInputStream.resolveClass(IOUtil.java:910)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1984)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459)
at com.hazelcast.internal.serialization.impl.defaultserializers.JavaDefaultSerializers$JavaSerializer.read(JavaDefaultSerializers.java:86)
... 17 common frames omitted

Test jar被部署到user-lib文件夹。我知道CacheKey类是正确部署的服务器端,因为我使用不同的服务来获取它,它的工作没有问题。做了很多其他的测试。唯一的问题是EntryExpiredListener。

Hazelcast服务器和JVM客户端都运行在我的本地机器上,单个节点。Hazelcast版本4.2.1

所以一切都在同一个jar中,我知道类是部署在服务器端。我错过了什么?

通过为HazelcastClient设置类加载器来解决这个问题:

ClientConfig clientConfig = new ClientConfig();
clientConfig.setClassLoader(TestService.class.getClassLoader());

我调试了Hazelcast代码,注意到它试图使用NarClassLoader来加载类。NarClassLoader来自我正在使用的Apache Nifi框架。Nifi将代码打包成。nar文件,而不是。jar文件。

不知道为什么它会尝试使用这个类加载器只为EntryExpiredListener。其他服务正在工作,这意味着它正在使用不同的类加载器并成功加载类。