Protocol Buffer(Protobuf) 是一种高效的数据结构序列化的机制,同时也是一种结构化数据的存储格式。
序列化与反序列化
- 序列化:将数据结构或对象转换成二进制串的过程;
- 反序列化:将序列化后的二进制串转换成数据结构或对象的过程;
语法
/*
* 语法
*/
/*
* 指定 Protobuf 解析使用的版本,可以是 proto3 或 proto2
*/
syntax = "proto3";
/*
* message 定义中的每一个字段都有一个唯一标识,该标识用于在二进制格式中识别字段
* 字段的标识一旦使用就不要进行修改
* 当标识为 1 到 15 时,使用一个字节进行编码,字节信息中包含字段的标识以及类型
* 当标识为 16 到 2047 时,使用两个字节进行编码
* Field numbers in the range 16 through 2047 take two bytes. So you should reserve the numbers 1 through 15 for very frequently occurring message elements.
* 在编码的过程中,标识使用应当留有余地,便于将来扩展
* 标识最小的值是 1,最大的值为 2^29-1
* 不能使用的标识为 19000 到 19999
* 不能再使用已经被 reserved 的标识
*/
/*
定义 message 的语法:
message ${MessageName} {
${Scalar Value Type} ${FieldName1} = ${Tag Number1};
.
.
.
${Scalar Value Type} ${FieldNameN} = ${Tag NumberN};
}
*/
message MessageTypes {
/*
* 标量值类型
*/
string stringType = 1; // 字符串可以是 UTF-8 编码,也可以是一个 7 比特的 ASCII 字符,默认为“”
// 数值类型,默认为 0
int32 int32Type = 2; // 使用变量长度进行编码,如果是负数,请使用 sint32
int64 int64Type = 3; // 使用变量长度进行编码,如果是负数,请使用 sint64
uint32 uInt32Type = 4; // 使用变量长度进行编码
uint64 uInt64Type = 5; // 使用变量长度进行编码
sint32 sInt32Type = 6; // 使用变量长度进行编码,处理负数更高效
sint64 sInt64Type = 7; // 使用变量长度进行编码,处理负数更高效
fixed32 fixed32Type = 8; // 变量总是占 4 个字节,当值大于 2^28 时,比使用 uint32 更有效率
fixed64 fixed64Type = 9; // 变量总是占 8 个字节,当值大于 2^56 时,比使用 uint64 更有效率
sfixed32 sfixed32Type = 10; // 变量总是占 4 个字节
sfixed64 sfixed64Type = 11; // 变量总是占 8 个字节
bool boolType = 12; // 布尔类型,默认为 false
bytes bytesType = 13; // 可包含任意长度的字节数组,默认为长度为 0 的字节数组
double doubleType = 14;
float floatType = 15;
enum Week {
UNDEFINED = 0; // 第 1 个值
SUNDAY = 1;
MONDAY = 2;
TUESDAY = 3;
WEDNESDAY = 4;
THURSDAY = 5;
FRIDAY = 6;
SATURDAY = 7;
}
Week wkDayType = 16;
/*
* 定义标量值类型的集合
* Syntax: repeated ${ScalarType} ${name} = TagValue
*/
repeated string listOfString = 17; // List[String]
}
/*
* 在其它 message 中使用已定义的 message
*/
message Person {
string fname = 1;
string sname = 2;
}
message City {
Person p = 1;
}
/*
* 嵌套的 message 定义
*/
message NestedMessages {
message FirstLevelNestedMessage {
string firstString = 1;
message SecondLevelNestedMessage {
string secondString = 2;
}
}
FirstLevelNestedMessage msg = 1;
FirstLevelNestedMessage.SecondLevelNestedMessage msg2 = 2;
}
/*
* .proto 文件的引入
*/
// one.proto
// message One {
// string oneMsg = 1;
// }
// two.proto
// import "myproject/one.proto"
// message Two {
// string twoMsg = 2;
// }
/*
* 高级知识点
*/
/*
* message 发生改变时,永远不要修改或使用已经删除字段的标识
*/
/*
* 使用 reserved 保留已删除的标识或字段名
*/
message ReservedMessage {
reserved 0, 1, 2, 3 to 10; // 这里的标识不可再使用
reserved "firstMsg", "secondMsg", "thirdMsg"; // 这里的字段名不可再使用
}
/*
* 引用其它文件中定义的 message
*/
import "google/protobuf/any.proto";
message AnySampleMessage {
repeated google.protobuf.Any.details = 1;
}
/*
* OneOf
* 相同于 union,只能是其中一个
* 使用 oneof 的 message 不能被 repeated
*/
message OneOfMessage {
oneof msg {
string fname = 1;
string sname = 2;
};
}
/*
* Maps
* map 字段不能被 repeated
*/
message MessageWithMaps {
map<string, string> mapOfMessages = 1;
}
/*
* Packages
* 声明一个包名,防止同名的 message
* 语法:
package ${packageName};
访问方式
${packageName}.${messageName} = ${tagNumber};
*/
/*
* 在 RPC 系统中使用,其中可以定义方法
*/
message SearchRequest {
string queryString = 1;
}
message SearchResponse {
string queryResponse = 1;
}
service SearchService {
rpc Search (SearchRequest) returns (SearchResponse);
}
数据类型
Protobuf 内置的数据类型以及在 Go 中对应的数据类型:
Protobuf | Go |
---|---|
double | float64 |
float | float32 |
int32 | int32 |
int64 | int64 |
uint32 | uint32 |
uint64 | uint64 |
sint32 | int32 |
sint64 | int64 |
fixed32 | uint32 |
fixed64 | uint64 |
sfixed64 | int64 |
bool | bool |
string | string |
bytes | []byte |
书写规范
代码编写要遵循某种特定的规则,如 Python 的 PEP8,.proto
文件的内容也应该按照统一的格式,这样才能规范团队编码风格,易于他人理解。
- message 采用驼峰命名法且首字母大写;
message UserConfig {} // 符合
message user_config {} // 不符合
- 字段名采用下划线分隔命名法且均小写;
message Product {
string fasta_origin = 1;
} // 符合
message Product {
string FastaOrigin = 1
} // 不符合
- 枚举型命名格式与 message 相同,枚举值全部大写,并且用下划线分隔命名法;
enum Week {
MY_MONDAY = 0
} // 符合
enum Week {
MyMonday = 0
} // 不符合
- service 和方法名都采用驼峰命名法,并且首字母大写;
service Greeter {
rpc SayHello(HelloRequest) returns (HelloReply);
} // 符合
service Greeter {
rpc say_hello(HelloRequest) returns (HelloReply);
} // 不符合
与 JSON 的区别
Protobuf | JSON |
---|---|
由 Google 开发、用于序列化和反序列化结构化数据的高效编码方式 | 轻量型的数据交换格式 |
可自定义规则、方法的消息格式 | 仅仅只是一种消息格式 |
二进制格式 | 文本格式 |
支持的语言有限 | 绝大部分语言都支持 |
微服务间数据传输的格式 | WEB 应用与服务器间的传输格式 |
相同数据序列化后的数据量较小 | 相同数据序列化后的数据量较大 |
评论