Protobuf语法

gRPC推荐使用proto3,本节只介绍经常使用语法,更多高级使用姿式请参考官方文档java

Message定义

一个message类型定义描述了一个请求或相应的消息格式,能够包含多种类型字段。例如定义一个搜索请求的消息格式,每一个请求包含查询字符串、页码、每页数目。python

syntax = "proto3";

message SearchRequest {
    string query = 1;           // 查询字符串
    int32  page_number = 2;     // 页码
    int32  result_per_page = 3; // 每页条数
}

首行声明使用的protobuf版本为proto3git

//

字段类型声明

全部的字段须要前置声明数据类型,上面的示例指定了两个数值类型和一个字符串类型。除了基本的标量类型还有复合类型,如枚举、其它message类型等。golang

标识符Tags

能够看到,消息的定义中,每一个字段都有一个惟一的数值型标识符。这些标识符用于标识字段在消息中的二进制格式,使用中的类型不该该随意改动。须要注意的是,[1-15]内的标识在编码时只占用一个字节,包含标识符和字段类型。[16-2047]之间的标识符占用2个字节。建议为频繁出现的消息元素使用[1-15]间的标识符。若是考虑到之后可能或扩展频繁元素,能够预留一些标识符。objective-c

最小的标识符能够从1开始,最大到229 - 1,或536,870,911。不可使用[19000-19999]之间的标识符, Protobuf协议实现中预留了这些标识符。在.proto文件中使用这些预留标识号,编译时就会报错。数组

字段规则

  • repeated:标识字段能够重复任意次,相似数组ruby

  • proto3不支持proto2中的required和optionalide

添加更多message类型

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

syntax = "proto3";

// SearchRequest 搜索请求
message SearchRequest {
    string query = 1;           // 查询字符串
    int32  page_number = 2;     // 页码
    int32  result_per_page = 3; // 每页条数
}

// SearchResponse 搜索响应
message SearchResponse {
    ...
}

添加注释

//

保留字段与标识符

可使用reserved关键字指定保留字段和保留标识符:

message Foo {
    reserved 2, 15, 9 to 11;
    reserved "foo", "bar";
}

注意,不能在一个reserved声明中混合字段名和标识符。

.proto文件编译结果

.proto.proto
.proto.h.cc.javaBuilder.proto.pb.go.rbBuilder.protopbobjc.hpbobjc.m.cs

各类语言的更多的使用方法请参考官方API文档

数据类型

这里直接引用官方文档的描述:

.proto C++ Java Python Go Ruby C#
double double double float float64 Float double
float float float float float32 Float float
int32 int32 int int int32 Fixnum or Bignum int
int64 int64 long ing/long[3] int64 Bignum long
uint32 uint32 int[1] int/long[3] uint32 Fixnum or Bignum uint
uint64 uint64 long[1] int/long[3] uint64 Bignum ulong
sint32 int32 int intj int32 Fixnum or Bignum int
sint64 int64 long int/long[3] int64 Bignum long
fixed32 uint32 int[1] int uint32 Fixnum or Bignum uint
fixed64 uint64 long[1] int/long[3] uint64 Bignum ulong
sfixed32 int32 int int int32 Fixnum or Bignum int
sfixed64 int64 long int/long[3] int64 Bignum long
bool bool boolean boolean bool TrueClass/FalseClass bool
string string String str/unicode[4] string String(UTF-8) string
bytes string ByteString str []byte String(ASCII-8BIT) ByteString

关于这些类型在序列化时的编码规则请参考 Protocol Buffer Encoding.

[1] java

[2] all

[3] 64

[4] Python

默认值

  • 字符串类型默认为空字符串

  • 字节类型默认为空字节

  • 布尔类型默认false

  • 数值类型默认为0值

  • enums类型默认为第一个定义的枚举值,必须是0

针对不一样语言的默认值的具体行为参考 generated code guide

枚举(Enum) TODO

使用其它Message

message SearchResponse {
    repeated Result results = 1;
}

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

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

导入定义(import)

可使用import语句导入使用其它描述文件中声明的类型

import "others.proto";
-I / --proto_path

Message嵌套

message SearchResponse {
    message Result {
        string url = 1;
        string title = 2;
        repeated string snippets = 3;
    }
    repeated Result results = 1;
}
Parent.Type
message SomeOtherMessage {
    SearchResponse.Result result = 1;
}

支持多层嵌套:

message Outer {                // Level 0
    message MiddleAA {         // Level 1
        message Inner {        // Level 2
            int64 ival = 1;
            bool  booly = 2;
        }
    }
    message MiddleBB {         // Level 1
        message Inner {        // Level 2
            int32 ival = 1;
            bool  booly = 2;
        }
    }
}

Message更新 TODO

Map类型

proto3支持map类型声明:

map<key_type, value_type> map_field = N;

message Project {...}
map<string, Project> projects = 1;
repeated

包(Packages)

.protopackage
syntax = "proto3";
package foo.bar;
message Open {...}

在其余的消息格式定义中可使用包名+消息名的方式来使用类型,如:

message Foo {
    ...
    foo.bar.Open open = 1;
    ...
}

在不一样的语言中,包名定义对编译后生成的代码的影响不一样:

Openfoo::baroption jave_packageoption go_packageFoo.Baroption csharp_namespace

定义服务(Service)

.protoSearchRequestSearchResponse.proto
service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse) {}
}

生成的接口代码做为客户端与服务端的约定,服务端必须实现定义的全部接口方法,客户端直接调用同名方法向服务端发起请求。比较蛋疼的是即使业务上不须要参数也必须指定一个请求消息,通常会定义一个空message。

选项(Options)

google/protobuf/descriptor.proto

一些选项是文件级别的,意味着它能够做用于顶层做用域,不包含在任何消息内部、enum或服务定义中。一些选项是消息级别的,能够用在消息定义的内部。固然有些选项能够做用在字段、enum类型、enum值、服务类型及服务方法中。可是到目前为止,并无一种有效的选项能做用于这些类型。

一下是一些经常使用的选择:

java_packagejava_outer_classnameobjc_class_prefix

基本规范

.proto
  • 结构定义包括:message、service、enum

  • rpc方法定义结尾的分号无关紧要

Message命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式

message SongServerRequest {
    required string song_name = 1;
}

Enums类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式

enum Foo {
    FIRST_VALUE = 1;
    SECOND_VALUE = 2;
}

Service与rpc方法名统一采用驼峰式命名

详解Go语言编译结果 TODO

message对应golang中的struct,编译生成go代码后,字段名会转换为驼峰式

编译

.protoprotoc

运行命令:

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 --javanano_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto

这里只作参考就好,具体语言的编译实例请参考详细文档,其中,Go语言的使用姿式会在其它章节详细说明:

吐槽: 照着官方文档一步步操做不必定成功哦!

更多

  • Any 消息类型

  • Oneof 字段

  • 自定义Options

这些用法在实践中不多使用,这里不作详细介绍,尤为自定义选项设计高级用法,有须要请参考官方文档。

参考

本系列示例代码