解决Hazelcast ReplicatedMap中ClassCastException与BINARY内存格式的兼容性问题

解决Hazelcast ReplicatedMap中ClassCastException与BINARY内存格式的兼容性问题

本文深入探讨了Hazelcast 4.2.5版本中,当Replicatedmap配置为InMemoryFormat.BINARY并启用统计功能时,可能出现的ClassCastException。该异常源于Hazelcast内部在收集指标时,尝试将String类型的数据强制转换为内部的HeapData格式。教程将详细分析问题根源,并提供将ReplicatedMap泛型声明为Data的解决方案,同时讨论其对应用代码的影响及相关最佳实践。

Hazelcast ReplicatedMap与ClassCastException:深入理解InMemoryFormat.BINARY

在使用hazelcast的replicatedmap时,尤其当配置了inmemoryformat.binary并启用了统计功能 (setstatisticsenabled(true)) 时,可能会遇到Java.lang.classcastexception。该异常通常发生在hazelcast内部尝试收集replicatedmap的运行时指标时,日志中会显示类似以下内容:

java.lang.ClassCastException: class java.lang.String cannot be cast to class com.hazelcast.internal.serialization.impl.HeapData     ...     at com.hazelcast.replicatedmap.impl.ReplicatedMapService.provideDynamicMetrics(ReplicatedMapService.java:387)     at com.hazelcast.replicatedmap.impl.ReplicatedMapService.getStats(ReplicatedMapService.java:357)     at com.hazelcast.replicatedmap.impl.ReplicatedMapService.getLocalReplicatedMapStats(ReplicatedMapService.java:197)     at com.hazelcast.replicatedmap.impl.LocalReplicatedMapStatsProvider.getLocalReplicatedMapStats(LocalReplicatedMapStatsProvider.java:85)     ...

信息可以看出,问题发生在LocalReplicatedMapStatsProvider.getLocalReplicatedMapStats方法内部,具体是在尝试获取内部数据记录的堆内存开销时。原始的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.replicatedmap.ReplicatedMap;  public class HazelcastConfigExample {      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);          HazelcastInstance hz = Hazelcast.getOrCreateHazelcastInstance(config);          ReplicatedMapConfig replicatedMapConfig =                 config.getReplicatedMapConfig("rogueUsers");          // 关键配置:内存格式为BINARY,并启用统计         replicatedMapConfig.setInMemoryFormat(InMemoryFormat.BINARY);         replicatedMapConfig.setAsyncFillup(true);         replicatedMapConfig.setStatisticsEnabled(true); // 导致问题的根源之一         replicatedMapConfig.setSplitBrainProtectionName("splitbrainprotection-name");          // 问题所在:ReplicatedMap泛型声明为String, String         ReplicatedMap<String, String> map = hz.getReplicatedMap("rogueUsers");         // map.addEntryListener(new RogueEntryListener()); // 假设存在监听器          return hz;     }      public static void main(String[] args) {         HazelcastInstance instance = setupHazelcastConfig();         // 可以在这里进行map操作,例如:         // ReplicatedMap<String, String> map = instance.getReplicatedMap("rogueUsers");         // map.put("user1", "statusA");         // System.out.println(map.get("user1"));     } }

剖析问题根源:内部数据表示与统计

当ReplicatedMapConfig的inMemoryFormat设置为InMemoryFormat.BINARY时,Hazelcast会将存储在Map中的对象序列化成二进制形式,即com.hazelcast.internal.serialization.Data接口的实现类(例如HeapData)。这种格式有助于节省内存和优化网络传输。

ClassCastException的发生,是由于在LocalReplicatedMapStats类的第85行左右,Hazelcast内部在计算内存使用量时,期望获取的是HeapData类型的内部记录,但实际获取到的却是string类型,导致了强制类型转换失败。

// 简化后的内部代码逻辑,来自LocalReplicatedMapStats boolean isBinary = (replicatedMapConfig.getInMemoryFormat() == InMemoryFormat.BINARY); if (isBinary) {   // 当 isBinary 为 true 时,期望 record.getValueInternal() 返回 HeapData   memoryUsage += ((HeapData) record.getValueInternal()).getHeapcost(); // <-- 异常发生处 }

这意味着,尽管我们配置了InMemoryFormat.BINARY,且期望所有数据都以二进制Data的形式存储,但在某些情况下(可能与ReplicatedMap的特定实现或版本有关),当Map的泛型声明为ReplicatedMap时,Hazelcast的统计模块在尝试访问内部数据时,未能正确地将其识别为HeapData,反而得到了原始的String对象,从而引发了类型转换错误。

解决方案:调整ReplicatedMap的泛型声明

为了解决这个ClassCastException,一种有效的解决方案是显式地将ReplicatedMap的泛型类型声明为com.hazelcast.internal.serialization.Data。Data是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.replicatedmap.ReplicatedMap; import com.hazelcast.internal.serialization.Data; // 引入Data类 import com.hazelcast.spi.serialization.SerializationService; // 引入SerializationService  public class HazelcastConfigFixedExample {      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);          HazelcastInstance hz = Hazelcast.getOrCreateHazelcastInstance(config);          ReplicatedMapConfig replicatedMapConfig =                 config.getReplicatedMapConfig("rogueUsers");          replicatedMapConfig.setInMemoryFormat(InMemoryFormat.BINARY);         replicatedMapConfig.set

© 版权声明
THE END
喜欢就支持一下吧
点赞9 分享