Protocol Buffer
proto 文件语法规则介绍
编译工具
.proto
文件建议使用 Visual Studio Code
proto 文件编辑插件:vscode-proto3
示例 proto 文件
// See README.md for information and build instructions.
//
// Note: START and END tags are used in comments to define sections used in
// tutorials. They are not part of the syntax for Protocol Buffers.
//
// To get an in-depth walkthrough of this file and the related examples, see:
// https://developers.google.com/protocol-buffers/docs/tutorials
// [START declaration]
syntax = "proto3"; // protobuf 版本
package tutorial; // 包名,隔离不同包 message 命名冲突问题
import "google/protobuf/timestamp.proto"; // 引入其他 proto 文件,可以使用 public、weak 关键字修饰
// [END declaration]
// [START java_declaration]
option java_multiple_files = true; // option 定义的可选字段
option java_package = "com.example.tutorial.protos";
option java_outer_classname = "AddressBookProtos";
// [END java_declaration]
// [START csharp_declaration]
option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
// [END csharp_declaration]
// [START go_declaration]
option go_package = "github.com/protocolbuffers/protobuf/examples/go/tutorialpb";
// [END go_declaration]
// [START messages]
message Person { // 定义 person message 类型对应的字段,会被 protoc 编译为不同语言的对应对象,如Java中的class、Go中的struct
string name = 1;
int32 id = 2; // Unique ID number for this person.
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phones = 4;
google.protobuf.Timestamp last_updated = 5;
}
// Our address book file is just one of these.
message AddressBook {
repeated Person people = 1;
}
// [END messages]
说明
- 命名:字段名、消息名、枚举类型名、map名、服务名等首字母必须是数据,后接字母、数字、或下划线
_
- message 的字段可以使用
repeated
修饰,表示重复,对应 Golang 的数组类型- 如:
message xxx { repeated double Field1 = 1; ...}
Oneof
一组字段最多允许一个字段出现map
键值类型:map<keyType, valueType> mapName = fieldNumber [ fieldOptions ]
deprecated=true
跟在字段后边,标识该字段已废弃
message 字段类型
- 数字类型:double、float、int32、int64、uint32、uint64、sint32、sint64
- 存储长度可变的浮点数、整数、无符号整数和有符号整数
- 存储固定大小的数字类型:fixed32、fixed64、sfixed32、sfixed64
- 布尔类型:
bool
- 字符串:
string
bytes
: 字节数组messageType
: 消息类型enumType
: 枚举类型- proto 数据类型和常见编程语言数据类型关系
.proto Type | C++ Type | Java Type | Go Type | PHP Type |
---|
double | double | double | float64 | float |
float | float | float | float32 | float |
int32 | int32 | int | int32 | integer |
int64 | int64 | long | int64 | integer/string |
uint32 | uint32 | int | uint32 | integer |
uint64 | uint64 | long | uint64 | integer/string |
sint32 | int32 | int | int32 | integer |
sint64 | int64 | long | int64 | integer/string |
fixed32 | uint32 | int | uint32 | integer |
fixed64 | uint64 | long | uint64 | integer/string |
sfixed32 | int32 | int | int32 | integer |
sfixed64 | int64 | long | int64 | integer/string |
bool | bool | boolean | bool | boolean |
string | string | String | string | string |
bytes | string | ByteString | []byte | string |
Reserved 忽略字段
Reserved
在 message 中忽略某些字段,如下
syntax = "proto3";
package abc;
message AllNormalypes {
reserved 2, 4 to 6;
reserved "field8", "field10";
double field1 = 1;
// float field2 = 2;
int32 field3 = 3;
// int64 field4 = 4;
// uint32 field5 = 5;
// uint64 field6 = 6;
sint32 field7 = 7;
// sint64 field8 = 8;
fixed32 field9 = 9;
// fixed64 field10 = 10;
sfixed32 field11 = 11;
}
enum 枚举类型
用来限定字段的取值范围,可以单独定义,也可以在 message 中:
enum PhoneType {
// option allow_alias = true; // 是否允许字段编号重复
MOBILE = 0;
HOME = 1;
WORK = 2;
}
packed
packed
修饰 repeated
字段或基本类型的 repeated
字段- 传统的多个
T-V-T-V-T-V
对存储 packed=true
的 repeated
字段存储方式,T-L-V-V-V
其他
message
字段类型也可以是另一个 messagemessage
可以嵌套google.protobuf.Any
字段允许处理嵌套数据- 时间
github.com/golang/protobuf/ptypes/timestamp.Timestamp
: time.Timegithub.com/golang/protobuf/ptypes/duration.Duration
:time.Duration
service
定义 rpc 接口
编译
#!/bin/bash
SRC_DIR=.
DST_DIR=.
protoc \
-I=$SRC_DIR \
-I/usr/local/include \
-I=$GOPATH/src \
--go_out=$DST_DIR \
# --plugin=$GOBIN/protoc-gen-go \
$SRC_DIR/proto/addressbook.proto
说明:
-I
protoc 搜索 import proto 的文件夹--*_out
目标代码输出目录,根据编程语言选择:--go_out
Golang 代码--cpp_out
C++ 代码--java_out
Java 代码--python_out
Python 代码--csharp_out
C# 代码--objc_out
ObjectC 代码--ruby_out
Ruby 代码--pho_out
PHP 代码
- 生成的数据结构会包括类和对应的方法,如
GetXXX()
等
序列化和反序列化
使用 proto.Marshal
和 proto.Unmarshal
方法
p := pb.Person{
Id: 1234,
Name: "John Doe",
Email: "jdoe@example.com",
Phones: []*pb.Person_PhoneNumber{
{Number: "555-4321", Type: pb.Person_HOME},
},
}
book := &pb.AddressBook{}
// ...
// Write the new address book back to disk.
out, err := proto.Marshal(book)
if err != nil {
log.Fatalln("Failed to encode address book:", err)
}
if err := ioutil.WriteFile(fname, out, 0644); err != nil {
log.Fatalln("Failed to write address book:", err)
}
- Read the existing address book.
in, err := ioutil.ReadFile(fname)
if err != nil {
log.Fatalln("Error reading file:", err)
}
book := &pb.AddressBook{}
if err := proto.Unmarshal(in, book); err != nil {
log.Fatalln("Failed to parse address book:", err)
}
详情参考: