这东西和什么语言无关,是中间接口描述语言IDL,

具体细节不说了,在参考链接里都有的,这里记录下我关注的细节

[toc]

字段

消息对象的字段 组成主要是:字段 = 字段修饰符 + 字段类型 +字段名 +标识号

类型是有个表格来描述具体的字节占用 (原来的表格有java我不太关注就去掉了)

.proto类型 C++类型 Go 备注
double double float64  
float float float32  
int32 int32   使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。
int64 int64   使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64。
uint32 uint32   Uses variable-length encoding.
uint64 uint64   Uses variable-length encoding.
sint32 int32   使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。
sint64 int64   使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。
fixed32 uint32   总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效。
fixed64 uint64   总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。
sfixed32 int32   总是4个字节。
sfixed64 int64   总是8个字节。
bool bool    
string string   一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。
bytes string   可能包含任意顺序的字节数据。 使用和string一样的,传参数也是string

如果字段更新类型,转换规则可以看字段更新部分

标识号分配

[1,15]之内的标识号在编码的时候会占用一个字节。[16,2047]之内的标识号则占用2个字节。所以应该为那些频繁出现的消息元素保留 [1,15]之内的标识号。要为将来有可能添加的/频繁出现的标识号预留一些标识号。

字段更新

如果一个已有的消息格式已无法满足新的需求——如,要在消息中添加一个额外的字段——但是同时旧版本写的代码仍然可用。不用担心!更新消息而不破坏已有代码是非常简单的。在更新时只要记住以下的规则即可。

  • 不要更改任何已有的字段的数值标识号。
  • 所添加的任何字段都必须是optional或repeated的。这就意味着任何使用“旧”的消息格式的代码序列化的消息可以被新的代码所解析,因为它们 不会丢掉任何required的元素。应该为这些元素设置合理的默认值,这样新的代码就能够正确地与老代码生成的消息交互了。类似地,新的代码创建的消息 也能被老的代码解析:老的二进制程序在解析的时候只是简单地将新字段忽略。然而,未知的字段是没有被抛弃的。此后,如果消息被序列化,未知的字段会随之一 起被序列化——所以,如果消息传到了新代码那里,则新的字段仍然可用。注意:对Python来说,对未知字段的保留策略是无效的。
  • 非required的字段可以移除——只要它们的标识号在新的消息类型中不再使用(更好的做法可能是重命名那个字段,例如在字段前添加“OBSOLETE_”前缀,那样的话,使用的.proto文件的用户将来就不会无意中重新使用了那些不该使用的标识号)。
  • 一个非required的字段可以转换为一个扩展,反之亦然——只要它的类型和标识号保持不变。
  • int32, uint32, int64, uint64,和bool是全部兼容的,这意味着可以将这些类型中的一个转换为另外一个,而不会破坏向前、 向后的兼容性。如果解析出来的数字与对应的类型不相符,那么结果就像在C++中对它进行了强制类型转换一样(例如,如果把一个64位数字当作int32来 读取,那么它就会被截断为32位的数字)。
  • sint32和sint64是互相兼容的,但是它们与其他整数类型不兼容。
  • string和bytes是兼容的——只要bytes是有效的UTF-8编码。
  • 嵌套消息与bytes是兼容的——只要bytes包含该消息的一个编码过的版本。
  • fixed32与sfixed32是兼容的,fixed64与sfixed64是兼容的。

说实话,兼容不是特别关注,主要关注标识号改动部分,最开始开发要做预留,然后改动标识号不要改动已有的,找缝隙加上就行

字段的操作(CURD)

一般来说,消息的字段会自动生成set_xxx方法

package message;                                                                                                                message MessageRequest {
    required string msg = 10;
}

对应的

message::MessageRequest::MessageRequest req;
req.set_msg("yoyoyo");

下面列举几个特殊场景

repeated字段的更新

repeat可以理解成数组,处理方法也多了几步, 会提供一个接口

package message;                                                                                                                
message Pair {
    required string key;
    required string value;
}
message MessageRequest {
    required string msg = 10;
    required int32 seq = 20;
    repeated Pair pairs = 30;
}

对应的修改

message::MessageRequest req;
std::vector<message::Pair> pairs;
for (auto& v: pairs) {
  //type: message::MessageRequest::field*  
  auto pair = req.add_pairs();
  pair->set_key('kk');
  pair->set_value('vv');
}
有人说,通过repeated字段来更新数据,当返回为空的时候,可能分不清是应该清空还是保持不变

需要加字段来纠正这个歧义,这里不细说了,感觉就是想要便捷(返回空)强行创造的歧义。约定好的话没啥问题,不需要加字段

package message;
message Pair {
    required string key;
    required string value;
}
message MessageRequest {
    required string msg = 10;
    required int32 seq = 20;
    repeated Pair pairs = 30;
    optional bool modify = 31; //如果是0个field modify是1那就是清空,modify是0那就是没更新
}

嵌套结构的消息, 生成了set_allocated_xxx方法, 没有普通的set_xxx方法

这里传进set_allocated_xxx 的对象必须是new好的,这个不太好用,protobuf内部会帮你delete,你自己也可以调用release_xx(最好别)

也可以用mutable_xx 内部帮你new好,你自己赋值,类似这样的

mutable_xx()->set_xx(/*xx*/);

也可以用copyfrom比较省心,其实都不太好用,尽量别嵌套

optional字段会生成has_xx方法 但proto3不支持怎么办

https://stackoverflow.com/questions/42622015/how-to-define-an-optional-field-in-protobuf-3

message Foo {
    int32 bar = 1;
    oneof optional_baz {
        int32 baz = 2;
    }
}

用oneof来封装一层 proto3新版本也支持optional了

merge

支持字段的merge操作,设置fieldMask


参考链接

  • 官方文档的翻译 https://colobu.com/2015/01/07/Protobuf-language-guide/
  • https://www.jianshu.com/p/e06ba6249edc 这篇感觉就是上一篇的细化
  • 这有个整理的更细致的 https://juejin.im/post/6844903687089831944
  • Repeated 修改 http://lambda.hk/protobuf/2015/06/05/protobuf-repeated/
  • repeated 歧义 https://blog.csdn.net/love_newzai/article/details/6929430
  • 嵌套 https://blog.csdn.net/xiaxiazls/article/details/50118161
    • copyFrom https://blog.csdn.net/u014088857/article/details/53291545
  • https://jergoo.gitbooks.io/go-grpc-practice-guide/content/chapter1/protobuf-go.html go的使用说明