Protobuf协议

282 阅读5分钟

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消息结构体是可以嵌套的

字段类型

image.png

应用

定义一个名为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)