
在使用hazelcast的replicatedmap时,如果配置了inmemoryformat.binary,可能会遇到一个频繁出现的java.lang.classcastexception。典型的错误信息如下:
java.lang.ClassCastException: class java.lang.String cannot be cast to class com.hazelcast.internal.serialization.impl.HeapData
这个异常通常发生在Hazelcast内部的指标收集周期中,具体堆栈信息指向com.hazelcast.replicatedmap.impl.LocalReplicatedMapStats类的getLocalReplicatedMapStats方法,尤其是在尝试计算内存使用量时:
if (isBinary) {
memoryUsage += ((HeapData) record.getValueInternal()).getHeapCost(); // <-- 异常发生在此处
}这表明,当Hazelcast尝试获取ReplicatedMap中条目的内部值并将其视为HeapData(com.hazelcast.internal.serialization.impl.HeapData是com.hazelcast.internal.serialization.Data的一个实现)时,它意外地得到了一个String对象,导致类型转换失败。
Hazelcast提供了多种内存存储格式,其中InMemoryFormat.BINARY和InMemoryFormat.OBJECT是两种主要的选择:
com.hazelcast.internal.serialization.Data是Hazelcast内部序列化机制的基本单元,它封装了对象的二进制表示。根据其Javadoc,Data存储了通过SerializationService.toData(Object)方法序列化后的对象的二进制形式。
尽管在配置中明确设置了replicatedMapConfig.setInMemoryFormat(InMemoryFormat.BINARY),但ReplicatedMap的实例却被声明为ReplicatedMap<String, String>:
ReplicatedMap<String, String> map = hz.getReplicatedMap("rogueUsers");Hazelcast通常能够透明地处理用户代码与内部二进制格式之间的转换。例如,当调用map.put("key", "value")时,Hazelcast会将"key"和"value"序列化为Data对象并存储;当调用map.get("key")时,它会从Data对象反序列化回String。
然而,在某些内部操作(如收集ReplicatedMap的统计信息)中,Hazelcast可能会直接访问存储在内存中的Data对象。当InMemoryFormat.BINARY被激活时,内部逻辑期望获取到的是Data类型的实例(或其子类如HeapData)。但由于ReplicatedMap被声明为ReplicatedMap<String, String>,在某些特定内部上下文中,可能导致内部方法(如record.getValueInternal())返回了反序列化后的String对象,而不是预期的Data对象,从而引发了ClassCastException。
简而言之,虽然用户代码通常与String类型交互,但当InMemoryFormat.BINARY启用时,Hazelcast内部存储和部分内部操作是基于Data对象的。声明ReplicatedMap<String, String>与这种内部机制在特定场景下产生了冲突。
解决此ClassCastException的关键在于确保ReplicatedMap的泛型类型与InMemoryFormat.BINARY的内部存储格式保持一致。这意味着,当选择二进制格式存储时,ReplicatedMap的键和值类型应声明为com.hazelcast.internal.serialization.Data。
将ReplicatedMap的声明从ReplicatedMap<String, String>更改为ReplicatedMap<Data, Data>:
ReplicatedMap<Data, Data> map = hz.getReplicatedMap("rogueUsers");以下是修正后的Hazelcast配置方法:
import com.hazelcast.config.Config;
import com.hazelcast.config.InMemoryFormat;
import com.hazelcast.config.JoinConfig;
import com.hazelcast.config.NetworkConfig;
import com.hazelcast.config.ReplicatedMapConfig;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.internal.serialization.Data; // 引入Data类
import com.hazelcast.map.listener.EntryAddedListener;
import com.hazelcast.map.listener.EntryRemovedListener;
import com.hazelcast.map.listener.EntryUpdatedListener;
import com.hazelcast.replicatedmap.ReplicatedMap;
import com.hazelcast.core.EntryEvent;
import java.io.Serializable;
public class HazelcastConfigSetup {
private static HazelcastInstance setupHazelcastConfig() {
Config config = new Config();
config.setInstanceName("rogueUsers");
NetworkConfig network = config.getNetworkConfig();
network.setPort(5701).setPortCount(20);
network.setPortAutoIncrement(true);
JoinConfig join = network.getJoin();
join.getMulticastConfig().setEnabled(true);
// join.getTcpIpConfig().setEnabled(true); // 如果使用TCP/IP,取消注释
HazelcastInstance hz = Hazelcast.getOrCreateHazelcastInstance(config);
ReplicatedMapConfig replicatedMapConfig =
config.getReplicatedMapConfig("rogueUsers");
replicatedMapConfig.setInMemoryFormat(InMemoryFormat.BINARY);
replicatedMapConfig.setAsyncFillup(true);
replicatedMapConfig.setStatisticsEnabled(true);
replicatedMapConfig.setSplitBrainProtectionName("splitbrainprotection-name");
// 核心修改:将ReplicatedMap的泛型类型改为Data, Data
ReplicatedMap<Data, Data> map = hz.getReplicatedMap("rogueUsers");
map.addEntryListener(new RogueEntryListener());
return hz;
}
// 示例EntryListener,需要根据实际业务逻辑调整
// 注意:如果ReplicatedMap的泛型改为Data,那么EntryListener中的EntryEvent也会是Data类型
// 在实际使用中,你需要手动对Data进行反序列化
static class RogueEntryListener implements EntryAddedListener<Data, Data>,
EntryUpdatedListener<Data, Data>, EntryRemovedListener<Data, Data>, Serializable {
@Override
public void entryAdded(EntryEvent<Data, Data> event) {
System.out.println("Entry Added: " + event.getKey() + " -> " + event.getValue());
// 示例:如何从Data对象获取原始值
// String keyStr = (String) event.getOldValue().getSerializationService().toObject(event.getKey());
// String valueStr = (String) event.getOldValue().getSerializationService().toObject(event.getValue());
}
@Override
public void entryRemoved(EntryEvent<Data, Data> event) {
System.out.println("Entry Removed: " + event.getKey() + " -> " + event.getOldValue());
}
@Override
public void entryUpdated(EntryEvent<Data, Data> event) {
System.out.println("Entry Updated: " + event.getKey() + " -> " + event.getOldValue() + " to " + event.getValue());
}
}
public static void main(String[] args) {
HazelcastInstance hz = setupHazelcastConfig();
ReplicatedMap<Data, Data> map = hz.getReplicatedMap("rogueUsers");
// 示例:如何存入和取出数据
String originalKey = "user:123";
String originalValue = "status:active";
// 将String序列化为Data对象存入Map
Data keyData = hz.getSerializationService().toData(originalKey);
Data valueData = hz.getSerializationService().toData(originalValue);
map.put(keyData, valueData);
System.out.println("Put: " + originalKey + " -> " + originalValue);
// 从Map中取出Data对象并反序列化为String
Data retrievedValueData = map.get(keyData);
if (retrievedValueData != null) {
String retrievedValue = hz.getSerializationService().toObject(retrievedValueData);
System.out.println("Get: " + originalKey + " -> " + retrievedValue);
}
hz.shutdown();
}
}当Hazelcast ReplicatedMap配置为InMemoryFormat.BINARY时,为了避免ClassCastException,关键在于确保ReplicatedMap的泛型类型与Hazelcast内部的Data存储格式保持一致。将ReplicatedMap<String, String>更改为ReplicatedMap<Data, Data>能够解决由于内部指标收集等操作期望Data对象而实际得到String对象所引发的类型转换错误。理解InMemoryFormat.BINARY的工作原理以及Data对象在Hazelcast内部的角色,对于正确配置和使用Hazelcast集群至关重要。
以上就是Resolving Hazelcast ReplicatedMap ClassCastException with InMemoryFormat.BINARY的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号