Protobuf协议
- Protobuf即Protocol Buffers,是Google公司开发的一种跨语言和平台的序列化数据结构的方式,是一个灵活的、高效的用于序列化数据的协议。
- 与XML和JSON格式相比,protobuf更小、更快、更便捷。protobuf是跨语言的,并且自带一个编译器(protoc),只需要用protoc进行编译,就可以编译成Java、Python、C++、C#、Go等多种语言代码,然后可以直接使用,不需要再写其它代码,自带有解析的代码。
- 只需要将要被序列化的结构化数据定义一次(在.proto文件定义),便可以使用特别生成的源代码(使用protobuf提供的生成工具)轻松的使用不同的数据流完成对结构数据的读写操作。甚至可以更新.proto文件中对数据结构的定义而不会破坏依赖旧格式编译出来的程序。
特点
- 语言无关、平台无关、可扩展,支持多种语言,如C++、Java、Python等。
- 体积小,序列化后的数据体积比较小,存储和传输非常高效。
- 加速数据序列化,性能高,编码和解码很快。
- 版本兼容,通过定义规则可以实现向前和向后的兼容。
- 定义结构化的数据格式。通过.proto文件进行数据结构定义。
- 自动生成代码,通过protoc编译器可以自动生成序列化和反序列化的代码。
- 类型安全,严格的结构化格式,类型检查更安全。
Protobuf的优点如下:
- 性能号,效率高
- 序列化后字节占用空间比XML少3-10倍,序列化的时间效率比XML快20-100倍。
- 有代码生成机制
- 将对结构化数据的操作封装成一个类,便于使用。
- 支持向后和向前兼容
- 当客户端和服务器同时使用一块协议的时候, 当客户端在协议中增加一个字节,并不会影响客户端的使用
- 支持多种编程语言
- Protobuf目前已经支持Java,C++,Python、Go、Ruby等多种语言。
Protobuf的缺点如下:
- 二进制格式导致可读性差
- 缺乏自描述(解析代码需要知道.proto文件,因为它包含字段名和序号的映射,否则无法知道数据意义)
协议格式
protobuf协议文件名后缀名为.proto。一个简单的protobuf协议如下:
1 syntax="proto3";
2
3 package protobuf.addressbook;
4
5 enum PhoneType
6 {
7 MOBILE = 0;
8 HOME = 1;
9 WORK = 2;
10 }
11
12 message Person
13 {
14 optional string name = 1;
15 optional uint32 age = 2;
16 optional string email = 3;
17
18 message PhoneNumber
19 {
20 optional string number = 1;
21 optional PhoneType type = 2;
22 }
23
24 repeated PhoneNumber phone = 4;
25
26 }
27
28
29 message AddressBook
30 {
31 repeated Person person = 1;
32 }
标识符
- syntax:标识使用的protobuf是哪个版本。上面表示使用的是3.x版本。
- package:标识生成目标文件的包名。在C++中表示的是命名空间。上面。表示生成的类和函数在protobuf命名空间的addressbook命令空间下。
- enum:表示一个枚举类型。会在目标.h文件中自动生成一个枚举类型。
- message:标识一条消息。会在目标文件中自动生成一个类。
- import:protobuf 接口文件可以像C语言的h文件一个,分离为多个,在需要的时候通过 import导入需要对文件
- service:定义这个proto文件的方法集合,类似于方法接口
- returns:返回响应,结合service使用
- rpc:定义方法的关键字,结合service使用
字段
- 字段格式:
role type name = tag [default value]
- role 有三种取值:
- required:该字段必须给值,不能为空。否则message被认为是未初始化的。如果试图建立一个未初始化的message将会抛出RuntimeException异常,解析未初始化的message会抛出IOException异常。
- optional:表示该字段是可选值,可以为空。如果不设置,会设置一个默认值。也可以自定义默认值。如果没有自定义默认值,会是用系统默认值。
- repeated:表示该字段可以重复,可等同于动态数组。
注意:required字段是永久性的,如果之后不使用该字段,或者该字段标识改为optional或repeated,那么使用就接口读取新协议时,如果发现没有该字段,会认为该消息不完整,会拒收或者丢弃该消息。message消息结构体是可以嵌套的
字段类型
应用
定义一个名为HelloService的接口,该接口包含方法SayHello(HelloRequest) HelloResponse,使用rpc协议
# 创建文件 protos/server.hello.proto
syntax = "proto3";
package user ;
service HelloService {
rpc SayHello (HelloRequest) returns (HelloResponse){}
}
message HelloRequest {
}
message HelloResponse {
string msg = 1;
}
定义字符串类型的数组
repeated string ids = 1;
定义map,key为int32类型,value为string类型
map<int32, string> attrs = 1;
定义二维数组输入
message Response{
message Edge{
repeated int32 edges = 1;
}
repested Edge values = 1;
}
对于二维数组的定义,需要进行解析,例如,输入数据样例为:
a = [[1,2],[3,4],[5,6],[7,8]]
解析
points = _____pb2.Response()
for i in range(len(edge)):
d = points.values.add()
d.edges.extend(edge[i])
定义二维数组输出
message Res {
message Result{
repeated string bayes_network = 1;
}
repeated Result result = 1;
}
解析
a = []
for one in response.result:
b = []
for i in one.bayes_network:
b.append(i)
a.append(b)