0

0

Resolving Hazelcast ReplicatedMap ClassCastException with InMemoryFormat.BINARY

花韻仙語

花韻仙語

发布时间:2025-07-08 20:42:23

|

599人浏览过

|

来源于php中文网

原创

Resolving Hazelcast ReplicatedMap ClassCastException with InMemoryFormat.BINARY

本文旨在解决Hazelcast ReplicatedMap在使用InMemoryFormat.BINARY时遇到的java.lang.ClassCastException,具体表现为String无法转换为com.hazelcast.internal.serialization.impl.HeapData。该异常源于ReplicatedMap声明的泛型类型与Hazelcast内部二进制数据存储格式不匹配。解决方案是调整ReplicatedMap的泛型参数,使其与Hazelcast的Data序列化单元保持一致,从而确保在内部操作(如指标收集)期间的数据处理一致性。

理解Hazelcast中的ClassCastException

在使用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与Data对象

Hazelcast提供了多种内存存储格式,其中InMemoryFormat.BINARY和InMemoryFormat.OBJECT是两种主要的选择:

  • InMemoryFormat.OBJECT: 数据以其原始的Java对象形式存储在内存中。每次读写操作通常涉及对象的序列化和反序列化,但对于用户代码而言,直接操作的是Java对象。
  • InMemoryFormat.BINARY: 数据以其序列化后的二进制形式存储在内存中。这种格式通常更节省内存,并且在某些场景下能提高性能,因为它避免了不必要的反序列化。当使用此格式时,Hazelcast内部将对象转换为com.hazelcast.internal.serialization.Data的实例。

com.hazelcast.internal.serialization.Data是Hazelcast内部序列化机制的基本单元,它封装了对象的二进制表示。根据其Javadoc,Data存储了通过SerializationService.toData(Object)方法序列化后的对象的二进制形式。

根源分析:类型不匹配

尽管在配置中明确设置了replicatedMapConfig.setInMemoryFormat(InMemoryFormat.BINARY),但ReplicatedMap的实例却被声明为ReplicatedMap

ReplicatedMap 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,在某些特定内部上下文中,可能导致内部方法(如record.getValueInternal())返回了反序列化后的String对象,而不是预期的Data对象,从而引发了ClassCastException。

Bandy AI
Bandy AI

全球领先的电商设计Agent

下载

简而言之,虽然用户代码通常与String类型交互,但当InMemoryFormat.BINARY启用时,Hazelcast内部存储和部分内部操作是基于Data对象的。声明ReplicatedMap与这种内部机制在特定场景下产生了冲突。

解决方案

解决此ClassCastException的关键在于确保ReplicatedMap的泛型类型与InMemoryFormat.BINARY的内部存储格式保持一致。这意味着,当选择二进制格式存储时,ReplicatedMap的键和值类型应声明为com.hazelcast.internal.serialization.Data。

将ReplicatedMap的声明从ReplicatedMap更改为ReplicatedMap

ReplicatedMap 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 map = hz.getReplicatedMap("rogueUsers");
        map.addEntryListener(new RogueEntryListener());

        return hz;
    }

    // 示例EntryListener,需要根据实际业务逻辑调整
    // 注意:如果ReplicatedMap的泛型改为Data,那么EntryListener中的EntryEvent也会是Data类型
    // 在实际使用中,你需要手动对Data进行反序列化
    static class RogueEntryListener implements EntryAddedListener,
            EntryUpdatedListener, EntryRemovedListener, Serializable {

        @Override
        public void entryAdded(EntryEvent 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 event) {
            System.out.println("Entry Removed: " + event.getKey() + " -> " + event.getOldValue());
        }

        @Override
        public void entryUpdated(EntryEvent event) {
            System.out.println("Entry Updated: " + event.getKey() + " -> " + event.getOldValue() + " to " + event.getValue());
        }
    }

    public static void main(String[] args) {
        HazelcastInstance hz = setupHazelcastConfig();
        ReplicatedMap 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();
    }
}

重要注意事项和最佳实践

  1. 手动序列化/反序列化: 当ReplicatedMap的泛型类型被声明为Data, Data时,用户代码在向Map中存入或从Map中取出数据时,需要显式地进行序列化和反序列化操作。这可以通过HazelcastInstance.getSerializationService().toData(Object)和HazelcastInstance.getSerializationService().toObject(Data)方法完成。
  2. 性能与可用性权衡: InMemoryFormat.BINARY通常能提供更好的内存效率和吞吐量,因为它避免了不必要的对象反序列化开销。然而,它也增加了用户代码的复杂性,需要手动处理Data对象。InMemoryFormat.OBJECT则提供了更简单的API,直接操作Java对象,但可能消耗更多内存。选择哪种格式应根据具体的应用场景和性能需求来决定。
  3. 类加载器问题: 原始堆栈信息中提到了String is in module java.base of loader 'bootstrap'; com.hazelcast.internal.serialization.impl.HeapData is in unnamed module of loader org.apache.catalina.loader.ParallelWebappClassLoader。虽然ClassCastException有时确实与类加载器隔离性问题有关(即同一个类名被不同的类加载器加载),但在本例中,根本原因是String和HeapData是两种完全不同的类型,且内部逻辑期望HeapData但收到了String。解决方案直接针对类型不匹配,而非类加载器隔离性。然而,在复杂的Web应用(如Tomcat部署)环境中,始终要注意类加载器的配置,以避免潜在的依赖冲突。
  4. 持续监控: 部署更改后,应密切监控Hazelcast的日志,特别是WARNING和SEVERE级别的消息,以确保没有新的异常或性能问题出现。

总结

当Hazelcast ReplicatedMap配置为InMemoryFormat.BINARY时,为了避免ClassCastException,关键在于确保ReplicatedMap的泛型类型与Hazelcast内部的Data存储格式保持一致。将ReplicatedMap更改为ReplicatedMap能够解决由于内部指标收集等操作期望Data对象而实际得到String对象所引发的类型转换错误。理解InMemoryFormat.BINARY的工作原理以及Data对象在Hazelcast内部的角色,对于正确配置和使用Hazelcast集群至关重要。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
如何配置Tomcat环境变量
如何配置Tomcat环境变量

配置Tomcat环境变量需要在系统中添加CATALINA_HOME变量,并将Tomcat的安装路径添加到PATH变量中。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

114

2023.10.26

idea如何集成Tomcat
idea如何集成Tomcat

idea集成Tomcat的步骤:1、添加Tomcat服务器配置;2、配置项目部署;3、运行Tomcat服务器;4、访问项目;5、注意事项;6、关闭Tomcat服务器。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

169

2024.02.23

怎么查看Tomcat源代码
怎么查看Tomcat源代码

查看Tomcat源代码的步骤:1、下载Tomcat源代码;2、在IDEA中导入Tomcat源代码;3、查看源代码;4、理解Tomcat的工作原理;5、参与社区和贡献;6、注意事项;7、持续学习和更新;8、使用工具和插件。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

97

2024.02.23

常见的tomcat漏洞有哪些
常见的tomcat漏洞有哪些

常见的tomcat漏洞有:1、跨站脚本攻击;2、跨站请求伪造;3、目录遍历漏洞;4、缓冲区溢出漏洞;5、配置漏洞;6、第三方组件漏洞。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

163

2024.02.23

tomcat日志乱码怎么解决
tomcat日志乱码怎么解决

tomcat日志乱码的解决办法:1、修改tomcat的日志编码设置;2、检查ide的编码设置;3、检查操作系统的编码设置;4、使用过滤器处理日志;5、检查外部系统的编码设置;6、检查文件编码方式等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

151

2024.02.23

weblogic和tomcat有哪些区别
weblogic和tomcat有哪些区别

weblogic和tomcat的区别:1、功能;2、性能;3、规模;4、价格;5、安全性;6、配置和管理;7、社区支持;8、集成能力;9、升级和更新;10、可靠性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

198

2024.02.23

tomcat和nginx有哪些区别
tomcat和nginx有哪些区别

tomcat和nginx的区别:1、应用领域;2、性能;3、功能;4、配置;5、安全性;6、扩展性;7、部署复杂性;8、社区支持;9、成本;10、日志管理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

233

2024.02.23

tomcat启动闪退怎么解决
tomcat启动闪退怎么解决

tomcat启动闪退的解决办法:1、检查java环境;2、检查环境变量配置;3、检查端口被占用;4、检查配置文件编码;5、检查启动时需要的配置文件;6、检查相关文件是否丢失;7、检查防火墙和杀毒软件设置。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

161

2024.02.23

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

HTML+CSS基础与实战
HTML+CSS基础与实战

共132课时 | 9.8万人学习

JS进阶与BootStrap学习
JS进阶与BootStrap学习

共39课时 | 3.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号