protobuf v3 中的字段默认值和空字段区分?

本贴最后更新于 1712 天前,其中的信息可能已经渤澥桑田

简介

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

  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    499 引用 • 1395 回帖 • 246 关注
  • Protobuf
    9 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...