protocol buffer是google的语言中立的、平台中立的,可扩展机制的,用于序列化结构化数据。
对比XML,更小、刚快、更简单。
简单的设计协议,通过自带工具转换成对应的语言代码,
协议是二进制协议,设计时只需要描述各个类的关系,简单明了。
syntax = "proto3"; // 指定使用proto3的语法,否则默认是proto2,该定义必须是文件的第一个非空的非注释行
// HelloWorldRequest消息定义了三个字段(名称/值对),对应着消息内容
// 每个字段都由字段限制、字段类型、字段名和编号四部分组成
message HelloWorldRequest {
string name = 1;
int32 age = 2;
}
double
float
int32
int64
boolean
string
byte
uint64
sint32
sint64
fixed32
fixed64
sfixed32
sfixed64
消息中的每一个字段都有一个独一无二的数值类型的编号,用来在消息的二进制格式中识别各个字段,一旦开始使用就不能再改变。
1-15使用一个字节编码
16-2047使用两个字节编码
所以将编号1-15留给频繁使用的字段
要为将来有可能添加的、频繁出现的标识号预留一些标识号
最小的标识号可以从1开始,最大到2^29 - 1, or 536,870,911。
不可以使用其中的[19000-19999]的标识号, Protobuf协议实现中对这些进行了预留。如果非要在.proto文件中使用这些预留标识号,编译时就会报错
required:必须赋值的字段
optional:可有可无的字段
repeated:可重复的字段(变长字段),重复使用任意次数(包括0次)
一个.proto文件可以定义多个消息类型
//
当你在某次更新消息中屏蔽或者删除了一个字段的话,未来的使用着可能在他们的更新中重用这个标签数字来标记他们自己的字段。
然后当他们加载旧的消息的时候就会出现很多问题,包括数据冲突,隐藏的bug等等。
指定这个字段的标签数字(或者名字,名字可能在序列化为JSON的时候可能冲突)标记为reserved来保证他们不会再次被使用。如果以后的人试用的话protobuf编译器会提示出错。
message Foo {
reserved 2, 15, 9 to 11;
reserved "foo", "bar";
}
// 一个reserved字段不能机油标签数字又有名字
string:空字符串
字节:空字节
布尔:false
数字类型:0
枚举:第一个定义的枚举值,且该值必须为0
重复字段:空
Corpus枚举第一个常量映射为0,每个枚举定义必须包含一个映射到0的常量作为第一个元素
设置可选参数allow_alias为true,可以在枚举结构中使用别名(两个值元素值相同)
枚举器常量必须在32位正数范围内
不建议使用负值
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
使用一个消息的定义作为另一个消息的字段类型
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
import 'other_protos.proto';
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
repeated Result results = 1;
}
// 在另一个消息中使用Result定义
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
不要更改任何现有字段的字段编号
添加新字段,必须是optional和repeated限定符,否则无法保证新老程序在互相传递消息时的兼容性
在原有消息中,不能移出已经存在的required字段,optional和repeated类型的字段可以被移除,但是他们之前使用的标签号必须被保留,不能被新的字段重用
int32、uint32、int64、uint64和bool等类型之间是兼容的,sint32和sint64是兼容的,string和bytes是兼容的,fixed32和sfixed32,以及fixed64和sfixed64之间是兼容的,这意味着如果想修改原有字段的类型时,为了保证兼容性,只能将其修改为与其原有类型兼容的类型,否则就将打破新老消息格式的兼容性
optional和repeated限定符也是互相兼容
any类型不需要在.proto文件中定义就可以直接使用的消息类型,使用前import google/protobuf/any.proto文件
import "google/protobuf/any.proto";
message ErrorStatus {
string message = 1;
repeated google.protobuf.Any details = 2;
}
Oneof
一个包含许多字段的消息,且最多只同时设置一个字段,可以使用oneof强制执行并节省内存
oneof字段只有最后被设置的字段才有效,即后面的set操作会覆盖前面的set操作
oneof不可以是repeated
反射api可以用在oneof字段
添加或删除oneof字段,如果检测到oneof字段的返回值是None/Not_Set,意味着oneof没有被设置或设置了一个不同的oneof版本,但无法区分这两种情况(向后兼容)
删除或添加字段到oneof,在消息序列化或解析后会丢失一些消息,一些字段会被清空
删除一个字段然后重新添加,在消息序列化或解析后会清除当前设置的oneof字段
分割或合并字段,同普通的删除字段操作
map map_field = N;
key_type可以是除浮点指针或bytes外的其他基本类型,value_type可以是任意类型
Map的字段不可以是重复的(repeated)
线性顺序和map值的的迭代顺序是未定义的,所以不能期待map的元素是有序的
maps可以通过key来排序,数值类型的key通过比较数值进行排序
线性解析或者合并的时候,如果出现重复的key值,最后一个key将被使用。从文本格式来解析map,如果出现重复key值则解析失败
向后兼容:在线性上是等价的,即使paotocol buffers没有实现maps数据结构也不会影响数据的处理
可以向.proto文件添加package可选说明符,以防止协议消息类型之间的名称冲突