.NET客户端检索Apache Ignite C++复杂类型触发网格映射请求失败异常
问题背景
使用Apache Ignite 2.14.0 Java服务端,搭配.NET Core 6.0 ASP.NET Core WebAPI及C++、C# Worker程序,已按跨平台互操作与序列化文档定义WorkerStatus类。两类客户端均可向缓存写入WorkerStatus数据,但.NET客户端仅能读取C#写入的数据,读取C++写入的数据时触发异常:
Apache.Ignite.Core.Binary.BinaryObjectException: Requesting mapping from grid failed for [platformId=1, typeId=959909104]
服务端配置的BinaryConfiguration如下:
<property name="binaryConfiguration"> <bean class="org.apache.ignite.configuration.BinaryConfiguration"> <property name="compactFooter" value="false"/> <property name="idMapper"> <bean class="org.apache.ignite.binary.BinaryBasicIdMapper"> <property name="lowerCase" value="true"/> </bean> </property> <property name="nameMapper"> <bean class="org.apache.ignite.binary.BinaryBasicNameMapper"> <property name="simpleName" value="true"/> </bean> </property> </bean> </property>
已尝试简化WorkerStatus类、调整包结构、移除可疑属性,怀疑C#与C++的TypeId计算不一致,现咨询三个问题:
- .NET Core 6.0与C++的跨平台兼容性是否可行?
- 是否有更复杂的跨平台序列化示例?
- 采用SQL方案能否规避该序列化问题?
问题解答
1. .NET Core 6.0与C++的跨平台兼容性完全可行
Apache Ignite原生支持多平台互操作,只要保证类型Id、字段映射、序列化配置完全一致,就能实现跨平台数据读写。你遇到的问题核心是C和.NET端的WorkerStatus类型Id计算不匹配,导致.NET客户端无法识别C写入的数据类型。
需要重点对齐以下配置:
- 两端的
BinaryBasicIdMapper规则:C++和.NET端都要启用lowerCase=true,确保类型名称的大小写处理统一。 - C++端的类型名称指定:通过
IGNITE_BINARY_TYPE_NAME宏设置的类型名称,必须和.NET端类名经BinaryBasicNameMapper处理后的结果完全一致。 - 字段名称映射:两端字段名经
BinaryBasicNameMapper(simpleName=true)处理后要一一对应,大小写规则统一。
2. 复杂跨平台序列化示例
以包含嵌套对象、枚举、集合的WorkerTask类型为例,各端实现如下:
Java服务端配置
显式注册类型并对齐映射规则:
BinaryConfiguration binaryCfg = new BinaryConfiguration(); binaryCfg.setCompactFooter(false); binaryCfg.setIdMapper(new BinaryBasicIdMapper(true)); binaryCfg.setNameMapper(new BinaryBasicNameMapper(true)); BinaryTypeConfiguration taskTypeCfg = new BinaryTypeConfiguration("WorkerTask"); taskTypeCfg.addFieldConfiguration(new BinaryFieldConfiguration("Id")); taskTypeCfg.addFieldConfiguration(new BinaryFieldConfiguration("Name")); taskTypeCfg.addFieldConfiguration(new BinaryFieldConfiguration("Status")); taskTypeCfg.addFieldConfiguration(new BinaryFieldConfiguration("SubTasks")); binaryCfg.setTypeConfigurations(Collections.singletonList(taskTypeCfg));
.NET端实现
[BinaryTypeName("WorkerTask")] public class WorkerTask { public int Id { get; set; } public string Name { get; set; } public TaskStatus Status { get; set; } public List<SubTask> SubTasks { get; set; } } public enum TaskStatus { Pending, Running, Completed } [BinaryTypeName("SubTask")] public class SubTask { public int SubTaskId { get; set; } public string Description { get; set; } }
C++端实现
enum TaskStatus { PENDING, RUNNING, COMPLETED }; class SubTask { public: int subTaskId; std::string description; IGNITE_BINARY_TYPE(SubTask) { ignite::binary::BinaryType<SubTask>::SetTypeName("SubTask"); ignite::binary::BinaryType<SubTask>::SetIdMapper(ignite::binary::BinaryBasicIdMapper(true)); ignite::binary::BinaryType<SubTask>::SetNameMapper(ignite::binary::BinaryBasicNameMapper(true)); ignite::binary::BinaryType<SubTask>::RegisterField<int>("subTaskId", &SubTask::subTaskId); ignite::binary::BinaryType<SubTask>::RegisterField<std::string>("description", &SubTask::description); } }; class WorkerTask { public: int id; std::string name; TaskStatus status; std::vector<SubTask> subTasks; IGNITE_BINARY_TYPE(WorkerTask) { ignite::binary::BinaryType<WorkerTask>::SetTypeName("WorkerTask"); ignite::binary::BinaryType<WorkerTask>::SetIdMapper(ignite::binary::BinaryBasicIdMapper(true)); ignite::binary::BinaryType<WorkerTask>::SetNameMapper(ignite::binary::BinaryBasicNameMapper(true)); ignite::binary::BinaryType<WorkerTask>::RegisterField<int>("id", &WorkerTask::id); ignite::binary::BinaryType<WorkerTask>::RegisterField<std::string>("name", &WorkerTask::name); ignite::binary::BinaryType<WorkerTask>::RegisterField<TaskStatus>("status", &WorkerTask::status); ignite::binary::BinaryType<WorkerTask>::RegisterField<std::vector<SubTask>>("subTasks", &WorkerTask::subTasks); } };
核心要求:各端类型名称、字段名称、映射规则完全统一,枚举类型的数值与名称对应一致。
3. 采用SQL方案可以规避序列化问题
使用Ignite SQL读写数据时,无需依赖二进制类型的序列化映射,只需将缓存配置为SQL表,通过列形式操作数据,各平台客户端用SQL语句访问,直接绕过类型Id不匹配的问题。
具体实现步骤:
- Java服务端配置缓存为SQL表:
<bean id="workerStatusCacheCfg" class="org.apache.ignite.configuration.CacheConfiguration"> <property name="name" value="WorkerStatusCache"/> <property name="queryEntities"> <list> <bean class="org.apache.ignite.cache.QueryEntity"> <property name="keyType" value="java.lang.Long"/> <property name="valueType" value="WorkerStatus"/> <property name="fields"> <map> <entry key="id" value="java.lang.Long"/> <entry key="workerName" value="java.lang.String"/> <entry key="status" value="java.lang.String"/> <entry key="lastUpdateTime" value="java.util.Date"/> </map> </property> <property name="tableName" value="WORKER_STATUS"/> </bean> </list> </property> </bean>
- .NET端SQL查询示例:
var sqlQuery = new SqlQuery(typeof(WorkerStatus), "SELECT id, workerName, status FROM WORKER_STATUS WHERE id = ?", 1L); var results = cache.Query(sqlQuery).ToList();
- C++端SQL操作示例:
ignite::SqlFieldsQuery query("SELECT id, workerName, status FROM WORKER_STATUS WHERE id = ?"); query.AddArgument(1L); ignite::FieldsQueryCursor cursor = cache.QueryFields(query); for (ignite::FieldsRow row : cursor) { int64_t id = row.Get<int64_t>(0); std::string workerName = row.Get<std::string>(1); std::string status = row.Get<std::string>(2); // 处理数据 }
这种方式下,各平台无需关心二进制序列化细节,按SQL表字段结构读写即可。
内容的提问来源于stack exchange,提问作者osmedd
相关产品推荐
相关产品推荐

