我一直在 Node.js 应用程序中使用版本为 3.1.2 的 redis npm 包。现在我正在尝试将 redis npm 包升级到 4.6.13,由于这是一次重大升级,过去适用于 3.1.2 版本的功能在 4.6.13 中已失效。新的redis服务器安装也已升级到最新可用版本7
问题主要出现在我尝试反序列化存储在redis中的protobuf数据时。
我创建了一个示例代码:
我使用的示例代码是:
设置原型消息:
const setApplicationInfo = (data) => {
try {
const applicationDetailMessage = new ApplicationDetails();
applicationDetailMessage.setEmail(data.email);
applicationDetailMessage.setHomephonenumber('');
applicationDetailMessage.setMobilephonenumber(data.primaryPhoneNumber);
applicationDetailMessage.setYearofbirth(data.yearOfBirth);
applicationDetailMessage.setFirstname(data.firstName);
applicationDetailMessage.setMiddlename(data.middleName);
applicationDetailMessage.setLastname(data.lastName);
applicationDetailMessage.setMonthofbirth(data.monthOfBirth);
applicationDetailMessage.setDayofbirth(data.dayOfBirth);
applicationDetailMessage.setSsn(data.ssn);
applicationDetailMessage.setStreetaddress(data.streetAddress1);
applicationDetailMessage.setCity(data.city);
applicationDetailMessage.setState(data.stateCode);
applicationDetailMessage.setZipcode(data.zipCode);
applicationDetailMessage.setResidencetype(data.residenceType);
// applicationDetailMessage.setResidencelengthinyears(data.durationOfResidenceYear);
// applicationDetailMessage.setResidencelengthinmonths(data.durationOfResidenceMonth);
const serializedData = applicationDetailMessage.serializeBinary();
return Buffer.from(serializedData);
} catch (err) {
throw new Error(err)
}
};
获取redis连接:
const getRedisConnection = async () => {
try {
const client = await createClient({
url: `redis://${process.env.REDIS_USER}:${process.env.REDIS_PASS}@${process.env.REDIS_HOST}:${process.env.REDIS_PORT}`,
// legacyMode: true
})
client.on('error', err => console.log('Redis Client Error', err))
await client.connect();
await client.ping();
return client;
} catch (err) {
throw err;
}
}
主要函数对原型进行序列化和反序列化
(async () => {
try {
const redisConn = await getRedisConnection();
const id = '1';
const data = {
email: '[email protected]',
primaryPhoneNumber: '4078790329',
dayOfBirth: 11,
monthOfBirth: 12,
yearOfBirth: 1996,
firstName: 'Vernon',
middleName: '',
lastName: 'McClelland',
ssn: '228412571',
streetAddress1: '4311 Grand Avenue',
city: 'Orlando',
stateCode: 'FL',
zipCode: '32810',
residenceType: 1,
durationOfResidenceYear: 2,
durationOfResidenceMonth: 1,
}
const message = setApplicationInfo(data)
const deserializedApplicationObj = ApplicationDetails.deserializeBinary(new Uint8Array(message)).toObject();
console.log('deserializedApplicationObj', deserializedApplicationObj);
await redisConn.set(`application_detail_${id}`, message);
const applicationRes = await redisConn.get(`application_detail_${id}`);
const applicationBufferData = Buffer.from(applicationRes, 'utf-8');
console.log('applicationBufferData', applicationBufferData);
if (applicationBufferData && applicationBufferData.length > 0) {
const applicationObj = ApplicationDetails.deserializeBinary(new Uint8Array(applicationBufferData)).toObject();
console.log('applicationObj', applicationObj);
}
} catch (err) {
throw err;
}
})()
这工作正常,并且成功反序列化应用程序对象
记录的应用程序对象是:
applicationObj {
email: '[email protected]',
homephonenumber: '',
mobilephonenumber: '4078790329',
yearofbirth: 32464879,
firstname: 'Vernon',
middlename: '',
lastname: 'McClelland',
monthofbirth: 12,
dayofbirth: 11,
ssn: '228412571',
streetaddress: '4311 Grand Avenue',
city: 'Orlando',
state: 'FL',
zipcode: '32810',
residencetype: 1,
residencelengthinyears: 0,
residencelengthinmonths: 0
}
现在在函数setApplicationInfo中,当我取消注释以下代码行时:
// applicationDetailMessage.setResidencelengthinyears(data.durationOfResidenceYear);
它抛出错误:
注:
在将序列化的原型缓冲区消息存储到 Redis 服务器之前,我正在检查反序列化是否适用于该特定的原型缓冲区,并且到目前为止它工作正常,
const deserializedApplicationObj = ApplicationDetails.deserializeBinary(new Uint8Array(message)).toObject();
console.log('deserializedApplicationObj', deserializedApplicationObj);
当我尝试反序列化从 Redis 服务器获取的数据时,它只会抛出错误
错误信息如下:
jspb.asserts.fail=function(a,b){for(var c=[],d=1;d<arguments.length;++d)c[d-1]=arguments[d];throw Error("Failure"+(a?": "+a:""),c);};jspb.asserts.assertInstanceof=function(a,b,c,d){for(var e=[],f=3;f<arguments.length;++f)e[f-3]=arguments[f];a instanceof b||jspb.asserts.doAssertFailure("Expected instanceof %s but got %s.",[jspb.asserts.getType(b),jspb.asserts.getType(a)],c,e);return a};
^
Error: Failure: Invalid wire type: %s (at position %s)
at jspb.asserts.fail (protos/node_modules/google-protobuf/google-protobuf.js:90:99)
at jspb.BinaryReader.nextField (/protos/node_modules/google-protobuf/google-protobuf.js:384:536)
at proto.demo.entities.application_pb.ApplicationDetails.deserializeBinaryFromReader (protos/build/application_pb.js:24459:17)
at proto.demo.entities.application_pb.ApplicationDetails.deserializeBinary (protos/build/application_pb.js:24447:66)
at /del_v2/redis/testredis.js:93:53
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
Node.js v20.9.0
导致此错误的原因可能是什么?仅当反序列化时存储在 Redis 中的缓冲区数据时才会出现问题。
我认为问题与以下几行有关:
const applicationRes = await redisConn.get(`application_detail_${id}`);
const applicationBufferData = Buffer.from(applicationRes, 'utf-8');
第一行返回一个包含二进制数据的字符串。 Node Redis 将对其进行编码,就好像它是转义的 C 样式字符串而不是 UTF-8 一样。
因此,如果该字节是可打印的 ASCII 字符,它将打印它。如果字节类似于换行符或回车符,它将分别返回
\n
或 \r
。而且,如果该字节没有通用的转义表示形式,它将使用 \x
和十六进制值对其进行转义。因此,包含值 1 的字节将返回 \x01
,包含值 255 的字节将返回 \xff
。
例如,如果我有字节
74 65 73 74 0A 01 02 03
,我将得到一串test\n\x01\x02\x03
。
Buffer.from
接收此文本并错误地对其进行解码,因为它不是正确的 UTF-8。您可能会侥幸逃脱,因为您的数据恰好如此排列。换句话说,这是一个幸运的巧合。如果您更改了数据,这可能会在其他地方发生。如果您在开发过程中没有发现这一点,这将导致很难追踪间歇性错误。所以,确实是一个幸运的巧合! 😉
你需要做的就是告诉 Node Redis 直接返回一个
Buffer
。然后就用那个Buffer
。您可以通过将 commandOptions
传递给您的呼叫来完成此操作:
const applicationBufferData = await redisConn.get(
commandOptions({ returnBuffers: true }),
`application_detail_${id}`
);
这有点笨重,但这将返回一个包含原始字节的
Buffer
,这就是你想要的。
完全披露,我还没有测试过这一点,我可能是错的。但我确实相信这就是正在发生的事情,而且我确实为 Redis 工作,所以我相当了解情况。但是,即使我错了,我希望它至少能提供有用的信息!
祝你好运!