Protobuf 语法

1. 基本规范

  • 文件以.proto做为文件后缀,除结构定义以外的语句以分号结尾
  • 结构定义能够包含:message、service、enum
  • message 定义结构体,service 定义方法
  • rpc方法定义结尾的分号无关紧要
  • Message 命名采用驼峰命名方式,字段命名采用小写字母加下划线分隔方式
  • Enums 类型名采用驼峰命名方式,字段命名采用大写字母加下划线分隔方式
  • Service与rpc方法名统一采用驼峰式命名
enum GenderType {
    SECRET = 0;
    FEMALE = 1;
    MALE = 2;
}

// 人
message Person {
    int64 id = 1;
    string name = 2;
    GenderType gender = 3;
    string number = 4;
}
复制代码

2. 字段规则

字段格式

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

限定修饰符

限定修饰符包含:required、optional、repeated数组

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

数据类型

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

Protobuf 数据类型 描述 打包
bool 布尔类型 1字节
double 64位浮点数 N
float 32位浮点数 N
int32 32位整数 N
uint32 无符号32位整数 N
int64 64位整数 N
uint64 64位无符号整数 N
$int32 32位整数,处理负数效率更高 N
$int64 64位整数,处理负数效率更高 N
fixed32 32位无符号整数 4
fixed64 64位无符号整数 8
$fixed32 32位整数,能以更高的效率处理负数 4
$fixed64 64位整数,能以更高的效率处理负数 8
string 只能处理ASCII字符 N
bytes 用于处理多字节的语言字符,如中文 N
enum 能够包含一个用户自定义的枚举类型uint32 N(uiint32)
message 能够包含一个用户自定义的消息类型 N
  • N 表示打包的字节并非固定,而是根据数据的大小或者长度
  • 关于fiex32 和 int32的区别:fixed32的打包效率比int32的效率高,可是使用的空间通常比int32多

字段名称

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

字段编码值

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

字段默认值

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

3. service 如何定义

  • 若是想要将消息类型用在 RPC 系统中,能够在 .proto文件中定义一个 RPC 服务接口,protocol buffer 编译器会根据所选择的不一样语言生成服务接口代码
  • 生成的接口代码做为客户端与服务端的约定,服务端必须实现定义的全部接口方法,客户端直接调用同名方法向服务端发起请求(即使业务上不须要参数也必须指定一个请求消息,通常会定义一个空message)

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

service SearchService {
	rpc Search(SearchRequest) returns (SearchResponse) {} } 复制代码

4. Message 如何定义

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

5. 添加更多 Message 类型

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

syntax = "proto3" // 声明使用的 protobuf 版本

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

message SearchResponse {

}
复制代码

6. 如何使用其余 Message

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

message SearchResponse {
	repeated Result results = 1;
}

message Result {
	string url = 1;
	string title = 2;
	repeated string snippets = 3;
}
复制代码

7. Message 嵌套的使用

支持嵌套消息,消息能够包含另外一个消息做为字段。也能够在消息内定义一个新的消息。spa

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

message SearchResponse {
	message Result {
		string url = 1;
		string title = 2;
		repeated string snippets = 3;
	}
	repeated Result results = 1;
}
复制代码

另外,还能够多层嵌套:orm

message Outer {
	message A {
		message Inner {
			int64 ival = 1;
			bool booly = 2;
		}
	}
	message B {
		message Inner {
			int64 ival = 1;
			bool booly = 2;
		}
	}
}
复制代码

8. proto3 的 Map 类型

  • proto3 支持 map 类型声明接口

  • 键、值类型能够是内置类型,也能够是自定义 message 类型

  • 字段不支持 repeated 属性

    map<key_type, value_type>map_field = N;
    
    message Project {...}
    map<string, Project>projects = 1;
    复制代码

9. .proto 文件编译

  • 经过定义好的 .proto 文件生成 Java、Python、Go、Ruby等代码,须要安装编译器 protoc
  • 使用 protobuf 编译器不一样的语言生成的代码格式不一样:
    • Go:生成一个 .pb.go 文件,每一个消息类型对应一个结构体
    • Java:生成一个 java 文件,每一个消息对应一个类,同时还有一个特殊的 Builder 类用于建立消息接口
    • ......

10. Import 导入定义

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

  • protobuf 接口文件能够经过 import 导入须要的文件,例如: import "example.proto"

  • protobuf 编译器会在 -I/ --proto_path 参数指定的目录中查找导入的文件,若是没有指定该参数,默认在当前目录中查找

11. 包的使用

在 proto 文件中使用 package 声明包名,避免命名冲突:

syntax = "proto3"
package foo.bar
message Open {...}
复制代码

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

message Foo {
	...
	foo.bar.Open open = 1;
	...
}
复制代码

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

  • Go:默认使用 package 名做为包名,除非指定了 option go_package 选项
  • Java:默认使用 package 名做为包名,除非指定了 option go_package 选项
  • Python:package 被忽略
相关文章
相关标签/搜索