在使用grpc的时候,proto的类型定义成int64,但结果展示却显示成string类型。刚开始还纳闷客户端说文档上定义int类型,为啥不和文档统一呢? 自信的我坚定的回答我定义的就是int类型的,统一的很。然后模拟完接口请求,打脸来得真快。

问题复现

  • 定义proto文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 音乐搜索项
message PartyMusicSearchItem {
    string name = 1;
    int32  fileSize = 2;
    string author = 3;
    string musicId = 4;
    int32 type = 6;
    int64 createdAt = 7;
    int64 lastTime = 8;
}
  • 返回值:
1
2
3
4
5
6
7
8
9
{
  "author": "张碧晨",
  "createdAt": "1640157765000",
  "fileSize": 10907,
  "lastTime": "1640157765000",
  "musicId": "61c2d2459a01c38927334b03",
  "name": "年轮",
  "type": 0
}

我们看到返回值中createdAt与lastTime已经变成是string类型的。

究其原因

翻阅proto的文档,可以看到,其实官方已经定义了某些类型及对应的返回值。明确的定义了int64类型的返回值是string. 但官方为什么这么做。其实是和javascriptnumber的实现有关,javascript中的number的最大精度是2的52次方,如果超过该数, 就会缺少精度。因此,grpc的处理方式是将int64处理成string类型。
github上相关issue的一个解释:
https://github.com/protocolbuffers/protobuf/issues/8331

这是另一个解释,本质上是和上面的一样的,因为中间会将proto文件转换成json,而json是基于javascript的标准的。因此,int64是不属于这个标准的,在这个标准体系里,int64会丢失精度。
https://github.com/golang/protobuf/issues/1280

解决方案

个人感觉只要使用json作为通信协议就无解,使用其他方式的通信协议就可以规避。但目前来说,json的通信协议还是最普遍的,因此也只能这样。 当然这个json不作为前后商通信,只是在同个项目里流转,比如golang,就可以使用json.NewEncoder相关的方式去处理。

参考文献

https://developers.google.com/protocol-buffers/docs/proto3#json https://github.com/protocolbuffers/protobuf/issues/8520
https://github.com/protocolbuffers/protobuf/issues/8331
https://github.com/golang/protobuf/issues/1280
https://www.jianshu.com/p/12b734ecd08b