Protobuf语法

Protobuf是Protocol Buffer的简称,它是Google公司开发的一种数据描述语言,用于描述一种轻便高效的结构化数据存储格式,是一种高效的数据格式,平台无关、语言无关、可扩展,常用于RPC系统和持续数据存储系统。

字段规则

字段格式

限定修饰符 | 数据类型 | 字段名称 | = | 字段编码值 | [字段默认值]

限定修饰符

required、optional、repeated

  • Required:表示是一个必须字段
  • Optional:表示一个可选字段。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段
  • Repeated:表示该字段可以包含0-N个元素。其中特性和optional一样,但是每一次可以包含多个值。可以看做是在传递一个数组的值

数据类型

Protobuf定义了一套基本数据类型:

.proto 类型 Notes C++ Type Java Type Python Type Go Type Ruby Type C# Type PHP Type Dart Type
double double double float float64 Float double float double
float float float float float32 Float float float double
int32 使用可变长度编码。编码负数的效率低 - 如果您的字段可能有负值,请改用sint32。 int32 int int int32 Fixnum or Bignum (as required) int integer int
int64 使用可变长度编码。编码负数的效率低 - 如果您的字段可能有负值,请改用sint64。 int64 long int/long[] int64 Bignum long integer/string[] Int64
uint32 使用可变长度编码 uint32 int int/long uint32 Fixnum or Bignum (as required) uint integer int
uint64 使用可变长度编码. uint64 long int/long uint64 Bignum ulong integer/string[] Int64
sint32 使用可变长度编码。签名的int值。这些比常规int32更有效地编码负数。 int32 int int int32 Fixnum or Bignum (as required) int integer int
sint64 使用可变长度编码。签名的int值。这些比常规int64更有效地编码负数。 int64 long int/long int64 Bignum long integer/string[] Int64
fixed32 总是四个字节。如果值通常大于228,则比uint32更有效。 uint32 int int/long uint32 Fixnum or Bignum (as required) uint integer int
fixed64 总是八个字节。如果值通常大于256,则比uint64更有效 uint64 long int/long[] uint64 Bignum ulong integer/string[] Int64
sfixed32 总是四个字节 int32 int int int32 Fixnum or Bignum (as required) int integer int
sfixed64 总是八个字节 int64 long int/long int64 Bignum long integer/string[] Int64
bool bool boolean bool bool TrueClass/FalseClass bool boolean bool
string 字符串必须始终包含UTF-8编码或7位ASCII文本,且不能超过232。 string String str/unicode string String (UTF-8) string string String
bytes 可以包含不超过232的任意字节序列。 string ByteString str []byte String (ASCII-8BIT) ByteString string List

字段名称

字段名称的命名与C、Java等语言的变量命名方式几乎是相同的
protobuf 建议字段的命名采用以下划线分隔的驼峰式

字段编码值

有了该值,通信双方才能互相识别对方的字段,相同的编码值,其限定修饰符和数据类型必须相同,编码值的取值范围为:1 ~ 2^32 (4294967296)
其中 1 ~ 15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低
1900 ~ 2000 编码值为 Google protobuf 系统内部保留值,建议不要在项目中使用

字段默认值

当在传递数据时,对于required数据类型,如果用户没有设置值,则使用默认值传递到对端

定义service

  • 如果想要将消息类型用在 RPC 系统中,可以在 .proto文件中定义一个 RPC 服务接口,protocol buffer 编译器会根据所选择的不同语言生成服务接口代码
  • 生成的接口代码作为客户端与服务端的约定,服务端必须实现定义的所有接口方法,客户端直接调用同名方法向服务端发起请求(即便业务上不需要参数也必须指定一个请求消息,一般会定义一个空message)

比如,想要定义一个 RPC 服务并具有一个方法,该方法接收 SearchRequest 并返回一个 SearchResponse,此时可以在.proto文件中进行如下定义:

1
2
3
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse) {}
}

定义Message

  • 一个 message 类型定义描述了一个请求或响应的消息格式,可以包含多种类型字段
  • 字段名用小写,转为 go 文件后自动变为大写,message 就相当于结构体

添加更多 Message 类型

一个 .proto 文件中可以定义多个消息类型,一般用于同时定义多个相关的消息,例如在同一个 .proto 文件中同时定义搜索请求和响应消息:

1
2
3
4
5
6
7
8
9
10
11
// 声明使用的 protobuf 版本
syntax = "proto3"

message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = ;
}

message SearchResponse {
}

使用其他 Message

message 支持嵌套使用,作为另一个 message 中的字段类型

1
2
3
4
5
6
7
8
9
message SearchResponse {
repeated Result results = 1;
}

message Result {
string url = 1;
string title = 2;
repeated string snippets = ;
}

Message 嵌套的使用

支持嵌套消息,消息可以包含另一个消息作为字段。也可以在消息内定义一个新的消息。

内部声明的 message 类型名称只可在内部直接使用:

1
2
3
4
5
6
7
8
message SearchResponse {
message Result {
string url = 1;
string title = 2;
repeated string snippets = ;
}
repeated Result results = 1;
}

多层嵌套:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
message Outer {
message A {
message Inner {
int64 ival = 1;
bool booly = 2;
}
}
message B {
message Inner {
int64 ival = 1;
bool booly = 2;
}
}
}

映射字段

每个映射字段会在Go的结构体中生成一个map[TKey]TValue类型的字段,其中TKey是字段的键类型TValue是字段的值类型。对于下面这个消息定义:

1
2
3
4
5
message Bar {}

message Baz {
map<string, Bar> foo = 1;
}

编译器生成Go结构体

1
2
3
4
type Baz struct {
Foo map[string]*Bar
}

枚举

给出如下枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
message SearchRequest {
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 1;
...
}

编译器将会生成一个枚举类型和一系列该类型的常量。

.proto 文件编译代码

  • 通过定义好的 .proto 文件生成 Go、PHP、Java、Python等代码,需要安装编译器 protoc。
  • 使用 protobuf 编译器不同的语言生成的代码格式不同,比如Go:生成一个 .pb.go 文件,每个消息类型对应一个结构体。

具体protoc安装和代码生成可见:
gRPC初体验

也有看到部分项目使用protobuf去替代json去进行前后端数据交换。


参考: