9.9下午一个人无聊地做着开发,W同学忽然说更新pb后mock里的 go:generate
语句报错,测试也过不了;想到前几天H同学更新了相关的pb,将某个结构中的 AsssetUuid
重命名成了 AsssetUuids
,关联PR已经合入hub的master分支(管理着所有项目的proto定义及生成的pb.go 文件)。建议W按新定义做修改,测试了下没啥问题。大家碰了下,H同学说他们项目里引用的确实是AsssetUuids
,而更新前hub里的pb和pb.go又确实是AsssetUuid
,而我们这边的关联的业务功能也表现正常,这下奇怪了......
H同学的server实现代码也不可能有什么魔法,确实是AsssetUuids
,并确认最近没改动。那我们客户端代码为啥没报错呢?导入最新的proto定义在 BloomRPC
调用Beta上的服务,发现AsssetUuid
方式调用返回的数据不对,AsssetUuids
方式则返回正常,难道是我们客户端代码写的有问题?
使用更新pb前的代码分支连接Beta上的服务进行远端测试,发现AsssetUuid
方式返回也正常,更奇怪了!切到新pb分支测试,远端调用也是正常。忽然想到是不是只和参数位置有关,和参数名无关,貌似之前在文档中看到过类似的说法,而且文档里也经常提到定义pb时不要轻易改变已有参数后的位置信息,保证向前兼容,这样似乎能解释大家看到的现象,下面就是进一步验证。
假设存在以下proto文件定义
// PbV1 开发人员L最初定义的版本并生成pb.go上传到hub中供客户端调用
message ListAssetsRequest {
uint64 user_id = 1;
repeated string asset_uuid = 2;
}
// PbV1.1 L修改了pb定义并生成对应pb.go在本地使用;可能未同步到hub仓库
message ListAssetsRequest {
uint64 user_id = 1;
repeated string asset_uuids = 2;
}
// PbV2 H同学开发功能时发现hub中的单数与server端实现的不一致,将hub中版本改进为复数并生成pb.go同步到hub
message ListAssetsRequest {
uint64 user_id = 1;
repeated string asset_uuids = 2;
}
// 客户端代码 需从V1迁移到V2;而线上版本正在引用V1版本
Proto是一种结构化数据序列化格式。
// go libary encoding/binary/varint.go中varint编码实现
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
func PutUvarint(buf []byte, x uint64) int {
i := 0
for x >= 0x80 {
buf[i] = byte(x) | 0x80
x >>= 7
i++
}
buf[i] = byte(x)
return i + 1
}
(1111 1111)补=> (1000 0000)反=>right shift 1(乘2) => (0000 0000) + 1 => (0000 0001)补 => 1
|2x+1|// [email protected]/encoding/protowire/wire.go中zig-zag编码实现
// DecodeZigZag decodes a zig-zag-encoded uint64 as an int64.
// Input: {…, 5, 3, 1, 0, 2, 4, 6, …}
// Output: {…, -3, -2, -1, 0, +1, +2, +3, …}
func DecodeZigZag(x uint64) int64 {
return int64(x>>1) ^ int64(x)<<63>>63
}
// EncodeZigZag encodes an int64 as a zig-zag-encoded uint64.
// Input: {…, -3, -2, -1, 0, +1, +2, +3, …}
// Output: {…, 5, 3, 1, 0, 2, 4, 6, …}
func EncodeZigZag(x int64) uint64 {
return uint64(x<<1) ^ uint64(x>>63)
}
// go libary encoding/binary/varint.go中zig-zag编码实现
// PutVarint encodes an int64 into buf and returns the number of bytes written.
// If the buffer is too small, PutVarint will panic.
func PutVarint(buf []byte, x int64) int {
ux := uint64(x) << 1
if x < 0 {
ux = ^ux
}
return PutUvarint(buf, ux)
}
Protocol Buffer 序列化原理大揭秘 - 为什么Protocol Buffer性能这么好?_专注分享 Android干货-CSDN博客_protobuf为什么快