简介
protobuf 是 google 开源的序列化工具,在微服务架构中是常见的 dto 构建工具。
protobuf v3
在 protobuf3 中,字段的的默认规则都是 optional 的,正是因为如此,我们在 marshal 的时候字段是可选的,但是在 unmarshal 的时候,所有的字段都是会被设置值的,如在 marshal 未设置字段在 unmarshal 的时候会被默认值填充,这时我们是没有办法区分字段是 nullState
还是 defaulteValue
。
如下:
syntax="proto3"
package dto
message Request {
string name = 1;
int32 age = 2;
string sex = 3;
}
message Response {
}
service Transport {
rpc Send(Request) returns(Response)
}
import "dto"
func TestNullState() {
req := &dto.Request{
Name: "joe",
Age: 23,
}
//假设拿到了grpc的client
resp, err := client.Send(req)
}
package service
import "dto"
type TransportService{}
func (transport *TransportService) Send(ctx context.Context, req *dto.Request) (resp *dto.Response, err error) {
//在这里我们拿到了dto.Requset,
//那么,如何判断req.Sex是未设置,还是设置了""呢?
//protobuf是不提共判断的,从v3开始。
}
判断 nullState 和 defaultValue
方案 1:使用特殊值判断
使用特殊值替代 nullState
,如年龄字段,age 显然不能为负,所以负数都可以替代 nullState,这是表示层的(represent)的 nullState。 但是如果是 description,这样的字段,如何表示 未设置状态
呢?
其次就是特殊值的耦合很深,编码不灵活。
方案二:显式定义 boolean 字段(不建议)
显式定义 bool 字段,那么 message 的字段数加倍,并且排版难看。
message Request {
string name = 1;
in32 age = 2;
string sex = 3;
bool has_name = 4;
bool has_age = 5
bool has_sex = 6;
}
方案三:使用 oneof 黑科技
message SampleMessage {
oneof test_oneof {
string name = 4;
SubMessage sub_message = 9;
}
}
oneof关键字和c语言中的union是一个意思,就是oneof中的所有字段只能同时set一个,set一个前一个将被抹去。
### 方案四:使用wrapper类型
使用wrapper类型来传递,是一个很好的方法,很nice。
```go
message String {//wrapper string.
string value = 1;
bool flag = 2;
}
这样虽然和 显式增加判断字段
相似,但是好看不是吗?且可维护性和扩展性更好。
方案五:使用 bitset 来判断字段是否 set
添加一个 bitset 类型字段来实现存储字段是否存储。至于 bitset 如何实现,这不重要(使用一个 int64 基本就已经够用了)。
message Request {
int64 fields_state = 1;
string name = 2;
int32 age = 3;
string sex = 4;
}
//通过位移运算来判断字段是否被设置。
以字段索引为索引,通过位移运算来判断字段是否被设置。
方案六:使用 json 传递数据,在 protobuf 中开洞
在 protobuf 里面打个洞,传递 json 数据,也就是 string。
message Request {
string content = 1;
}
在 grpc 后端,我们再对 content 字段进行 json.Unmarshal 来解析请求。
这样做是没有任何问题的,但是效率有点低。首先 grpc 是基于 htpp2 的,所以就需要经过 http 编解码和 protobuf 编解码,就是两次,再加上 json 的话就是 3 次,此时效率会降低。
Reference
[1] https://www.cnblogs.com/tohxyblog/p/8974763.html
[2] https://zhuanlan.zhihu.com/p/46603988
[3] https://stackoverflow.com/questions/42622015/how-to-define-an-optional-field-in-protobuf-3
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于