Protocol Buffer是google自定义的数据传输协议,目前已经被广泛用于服务端和客户端间的数据传输,清晰理解Protocol Buffer的使用以及语法就显得很重要,本文对Protocol Buffer语法分析是基于proto3.
目前使用最广泛的数据传输协议为JSON,JSON是一种轻量级的数据交换格式而且层次和结构比较简单和清晰,这里主要对比一下Protocol Buffer和JSON的对比,给出优势和劣势:
Protocol Buffer的使用流程总体可以分为三步,如下图所示:
在使用Protocol Buffer之前需要清楚理解其语法定义,本文对Protocol Buffer的语法解析是基于proto3版本
首先创建一个.proto文件,并且在文件中声明如下内容:
syntax = "proto3"; message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; } 复制代码
其中第一行标明当前proto使用的版本为proto3,后面定义了一个结构体SearchRequest,结构体中共3个属性。
在整个proto文件中分为基本类型和结构类型,其中结构类型主要为:
下面分别介绍一下不同结构的作用及规定:
message表示一个结构,类似于java中类,一个proto文件中可以声明多个message结构:
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; } message SearchResponse { ... } 复制代码
message可以引用不同proto文件中的message,只要在proto文件中的最上面声明import即可,如下所示:
import "myproject/other_protos.proto"; 复制代码
meesage 可以使用extend来继承另外一个message,并且使用其中的属性,这里注意一下由于每个message中需要对属性进行编号,在继承的时候需要注意编号,防止重复使用
enum使用很简单,直接在message中声明enum结构体并且将属性声明为对应的enum即可:
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; } 复制代码
上面代码中Corpus就是一个enum
在proto3中,enum第一个值必须为0,主要是为了和基础类型的默认值保持一致
map是proto3新加的,使用也很简单:
map<key_type, value_type> map_field = N; 复制代码
在proto2中可以使用repeated和message结构自定义map,如下所示
message Person { string key = 1; string value = 2; } repeated Person person = 4; 复制代码
proto中基础类型有很多,下面给出不同的基础类型对应的java中的类型,及其特点:
proto 类型 | java类型 | 备注 |
---|---|---|
double | double | |
float | float | |
int32 | int | 可变长度编码,如果有负值,可以使用sint32修饰 |
int64 | long | 可变长度编码,如果有负值,可以使用sint64修饰 |
uint32 | int | 可变长度编码 |
uint64 | int | 可变长度编码 |
sint32 | int | 可变长度编码,用来表示负值时效率比int32更高 |
sint64 | long | 可变长度编码,用来表示负值时效率比int64更高 |
fixed32 | int | 4个字节,当数值>2^28时效率比uint32高 |
fixed64 | long | 8个字节,当数值>2^56时效率比uint64高 |
sfixed32 | int | 4个字节 |
fixed64 | long | 8个字节 |
bool | boolean | |
string | String | UTF-8 encoded or 7-bit ASCII text, 长度不能超过2^32 |
bytes | ByteString | 长度不超过2^32,任意顺序的字节数据 |
其中部分基本类型修饰符长度不确定,主要采用了可变长度编码,这也是为什么Prorocol Buffer序列化后的数据字节更少,这个后面原理篇会介绍。
基础类型的默认值如下:
在proto3中,repeated的标量域默认情况下使用packed,也就是可变长度编码
先看下一个简单的proto文件:
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; } 复制代码
message SearchRequest 中的所有字段都声明了字段编号,这里需要注意:
日常开发过程中,由于需求的变更,往往需要增加字段,这就涉及到字段的扩充,字段扩充需要达到一个目的: 兼容
所以Protocol Buffer在字段扩充中定义了如下规则:
只要记住上述规则,就能完成字段扩充且老版本也能兼容
当proto文件编写后,就需要生成对应语言的源文件,生成操作如下:
protoc --proto_path=IMPORT_PATH --cpp_out=DST_DIR --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto 复制代码