如何实现NodeJS与ServiceStack C#应用的Redis数据序列化同步?
问题背景
原本有两个基于ServiceStack开发的C#应用A和B,二者通过共用Redis连接,借助ServiceStack的RedisClient实现数据交互,例如读取数据的代码如下:
var customers = client.GetAll<Customer>();
目前应用B已迁移至NodeJS,使用io-redis库,但应用A迁移难度极大,仍需与NodeJS版的B保持数据交互。当前存在的问题是:应用A读取NodeJS应用B写入Redis的数据时,必须手动进行反序列化,示例代码如下:
var userKeys = redisClient.GetAllKeys(); var users = new List<User>(); foreach (var key in userKeys) { var jsonData = redisClient.GetValue(key); if (!string.IsNullOrEmpty(jsonData)) { var user = JsonSerializer.Deserialize<User>(jsonData); users.Add(user); } }
请问是否可仅对NodeJS应用B进行修改,恢复二者间的自动序列化/反序列化通信?
可行方案:仅修改NodeJS应用B适配ServiceStack Redis格式
完全可以只调整NodeJS端的应用B,让它写入Redis的数据格式与原ServiceStack RedisClient保持一致,这样应用A就能像之前一样使用GetAll<T>()自动读取数据,无需手动反序列化。核心需要适配两个关键点:
1. 遵循ServiceStack的Redis键命名规则
ServiceStack RedisClient存储实体时,默认采用以下规则:
- 单个实体的键名格式为:
{C#类名}:{实体Id},例如User:100、Customer:5 - 会维护一个类型集合键(格式为
urn:{类名小写}),例如urn:user,集合内存储该类型所有实体的Id,GetAll<T>()会先读取这个集合获取所有Id,再批量拉取对应实体
在NodeJS的io-redis中,写入数据时需要:
- 用
{C#实体类名}:{Id}作为单个实体的键(类名必须和C#端完全一致,比如C#端是User,键就用User:{userId}) - 每次写入新实体时,用
SADD命令将实体Id添加到对应的类型集合键中,例如SADD urn:user {userId}
2. 适配ServiceStack的JSON序列化格式
ServiceStack的JSON序列化(基于ServiceStack.Text)和标准JSON的核心差异在于:
- 默认忽略空值属性,不会将空值序列化到JSON中
- 日期采用ISO8601格式(与标准JSON一致,无需额外处理)
- 多态类型会添加
__type字段(普通实体类无需处理,除非C#端有继承场景)
NodeJS端序列化时可以通过以下方式适配:
// 序列化时忽略空值,匹配ServiceStack.Text的默认行为 const jsonStr = JSON.stringify(user, (key, value) => { if (value === null || value === undefined) return undefined; return value; });
NodeJS端示例代码(io-redis)
const Redis = require('ioredis'); // 初始化Redis连接,替换为你的Redis配置 const redis = new Redis({ host: 'your-redis-host', port: 6379 }); // 写入User实体的方法,完全适配ServiceStack格式 async function saveUser(user) { const entityKey = `User:${user.id}`; // 匹配C#类名和Id字段 const typeCollectionKey = 'urn:user'; // ServiceStack的类型集合键 // 序列化实体为符合要求的JSON const jsonStr = JSON.stringify(user, (key, value) => { // 忽略空值 if (value === null || value === undefined) return undefined; return value; }); // 用事务确保操作原子性 const pipeline = redis.pipeline(); pipeline.set(entityKey, jsonStr); pipeline.sadd(typeCollectionKey, user.id); await pipeline.exec(); } // 批量读取User的方法(测试用) async function getAllUsers() { const typeCollectionKey = 'urn:user'; const userIds = await redis.smembers(typeCollectionKey); const entityKeys = userIds.map(id => `User:${id}`); return await redis.mget(entityKeys); }
验证效果
修改NodeJS应用B后,应用A可以直接使用原有代码读取数据,无需任何改动:
var users = client.GetAll<User>(); // 自动完成反序列化,和迁移前逻辑一致
内容的提问来源于stack exchange,提问作者noobDev2410
相关产品推荐
相关产品推荐

