Protocol Buffers (protobuf) 介绍
官方文档:https://protobuf.dev/programming-guides/proto3/
Protocol Buffers(简称 protobuf)是由 Google 开发的一种语言中立、平台中立、可扩展的序列化数据格式。它用于结构化数据的序列化和反序列化,常用于远程过程调用(RPC)、数据存储等场景。protobuf 提供了一种高效的方式来定义和传输结构化数据。
主要特点
1. 高效
protobuf 使用二进制格式进行序列化,比 JSON 或 XML 等文本格式更紧凑,占用更少的空间,传输和解析速度更快。
2. 跨语言支持
protobuf 支持多种编程语言,包括但不限于 C++、Java、Python、Go 和 C#。这种语言中立性使得不同语言编写的系统之间可以轻松地进行数据交换。
3. 易于定义
使用 .proto 文件定义数据结构,语法简单直观,可以方便地进行版本管理和扩展。
4. 向后兼容和向前兼容
protobuf 的设计考虑到了版本兼容性,可以轻松地进行数据结构的扩展而不影响现有系统。
Protocol Buffers 基本类型和默认值
在 Protocol Buffers (protobuf) 中,可以定义多种基本数据类型。这些类型可以用于定义消息字段的类型,并且每种类型都有其默认值。当消息中某个字段没有被赋值时,序列化后的消息会使用该字段的默认值。
Protocol Buffers 类型 | 说明 | 默认值 | Go 数据类型 | Python 数据类型 |
---|---|---|---|---|
int32 | 有符号 32 位整数 | 0 | int32 | int |
int64 | 有符号 64 位整数 | 0 | int64 | int |
uint32 | 无符号 32 位整数 | 0 | uint32 | int |
uint64 | 无符号 64 位整数 | 0 | uint64 | int |
sint32 | 有符号 32 位整数(采用 ZigZag 编码) | 0 | int32 | int |
sint64 | 有符号 64 位整数(采用 ZigZag 编码) | 0 | int64 | int |
fixed32 | 固定长度的无符号 32 位整数 | 0 | uint32 | int |
fixed64 | 固定长度的无符号 64 位整数 | 0 | uint64 | int |
sfixed32 | 固定长度的有符号 32 位整数 | 0 | int32 | int |
sfixed64 | 固定长度的有符号 64 位整数 | 0 | int64 | int |
float | 32 位浮点数 | 0.0 | float32 | float |
double | 64 位浮点数 | 0.0 | float64 | float |
bool | 布尔值 | false | bool | bool |
string | 字符串(UTF-8 或 7-bit ASCII 编码) | "" | string | str |
bytes | 任意字节序列 | b'' | []byte | bytes |
Protocol Buffers中的Option关键字
在 Protocol Buffers (protobuf) 中,option
关键字用于指定和定制 .proto 文件的各种配置和行为。
主要包括:
- 文件级别的
option
:用于指定语法版本、包名以及生成代码时的特定选项。 - 消息级别的
option
:用于配置特定消息的生成代码的包名和外部类名。 - 字段级别的
option
:用于为特定字段定义自定义行为,如标记字段为废弃或设置默认值(在 proto2 中)。
常见的option示例:
Option | 描述 | 示例用法 |
---|---|---|
syntax | 指定 .proto 文件的语法版本。 | syntax = "proto3"; |
package | 定义生成代码的包命名空间。 | package example; |
go_package | 指定生成的 Go 代码的包名。 | option go_package = "examplepb"; |
java_package | 指定生成的 Java 代码的包名。 | option java_package = "com.example.protos"; |
java_outer_classname | 指定生成的 Java 代码的外部类名。 | option java_outer_classname = "ExampleProto"; |
自定义字段选项 | 允许为特定消息字段定义自定义行为。 | int32 id = 1 [deprecated = true]; |
optional int32 id = 1 [default = 0]; (proto2) |
Protocol Buffers中的Message嵌套
嵌套Message的语法
在 protobuf 的消息定义中,可以在一个消息中定义另一个消息类型。语法如下:
message OuterMessage {
// 其他字段...
message InnerMessage {
// 内部消息类型的字段定义
}
// 可以在这里使用 InnerMessage 类型的字段
InnerMessage inner_message_field = 1;
}
示例说明
假设我们要定义一个包含订单信息的消息,并且每个订单包含多个商品。可以使用嵌套消息来表示这种关系:
message Order {
int32 order_id = 1;
string customer_name = 2;
message Product {
string product_id = 1;
string product_name = 2;
float price = 3;
}
repeated Product products = 3;
}
在这个示例中:
Order
是外部消息类型,包含了order_id
、customer_name
和products
字段。Product
是嵌套的消息类型,它包含了表示产品的字段:product_id
、product_name
和price
。repeated Product products
表示Order
消息中可以包含多个Product
类型的数据,使用repeated
关键字表示这是一个数组或者列表。
嵌套Message的实例化
以上面的为例,利用 命令生成代码文件后,如何实例化嵌套对象Product呢?
引入生成的代码文件后,实例化如下所示
product := proto.Order_Product{
ProductId: 1,
ProductName: "phone",
Price: 1000,
}
嵌套Message的优点
- 组织性和清晰性:可以更清晰地表示复杂的数据结构和关系。
- 模块化:可以将相关的消息类型放在一起,提高代码的模块化程度。
- 封装性:内部消息类型可以更好地隐藏实现细节,只暴露必要的接口。
Protocol Buffers中的枚举类型(enum)
在 Protocol Buffers (protobuf) 中,枚举类型用于定义一组命名的常量集合。它们提供了一种有效管理常量值的方式,使得消息结构更具可读性和可维护性。
定义枚举类型
在 protobuf 中,定义枚举类型的语法如下:
message YourMessage {
Status status = 1;
}
enum Status {protobuf
ACTIVE = 0;
ARCHIVED = 1;
}
示例说明
在 protobuf 中,定义枚举枚举的示例如下:
message Strudent {
string name = 1;
int32 age = 2;
Gender gender = 3;
}
enum Gender {
MALE = 0;
FEMALE = 1;
}
如何使用
利用命令生成对应的代码文件后,使用方法如下:
可以利用proto.Gender_MALE或者proto.Gender_FEMALE
,不推荐使用Gender: 0
和Gender: 1
,
student := proto.Strudent{
Age: 18,
Name: "张三",
Gender: proto.Gender_MALE, // 或 proto.Gender_FEMALE, 不推荐Gender: 0/1,
}
Protocol Buffers中的Map
在 Protocol Buffers (protobuf) 中,可以使用 map
类型来定义键值对集合。这种类型在描述键值对关系时非常有用,可以在消息中表示映射结构,如字典或哈希表。使用 map
类型可以方便地存储和检索键值对数据,提高了消息结构的灵活性和表达能力。
定义 map 类型
在 protobuf 中,定义 map
类型的语法如下:
组件 | 描述 |
---|---|
key_type | 表示映射中键的类型,可以是基本数据类型(例如 int32 、string )或枚举类型。 |
value_type | 表示映射中值的类型,可以是任何支持的数据类型(例如 int32 、string 、消息类型)。 |
map_field | 消息中存储键值对映射的字段名。 |
field_number | 在序列化和反序列化过程中用来唯一标识字段的编号。 |
message YourMessage {
map<key_type, value_type> map_field = field_number;
}
示例说明
在 protobuf 中,定义枚举Map的示例如下:
message Strudent {
string name = 1;
int32 age = 2;
map<string, int32> grade = 3;
}
如何使用
利用命令生成对应的代码文件后,使用方法如下:
student := proto.Strudent{
Age: 18,
Name: "张三",
Grade: map[string]int32{
"Chinese": 100,
"English": 100,
},
}
Protocol Buffers中的Timestamp
在 Protocol Buffers(protobuf)中,Timestamp 是一个特定的消息类型,用来表示时间戳信息。它通常与其他消息一起使用,用于记录事件发生的具体时间。
示例说明
在 protobuf 中,定义枚举Timestamp 的示例如下:
import "google/protobuf/timestamp.proto";
message Strudent {
string name = 1;
int32 age = 2;
google.protobuf.Timestamp current = 3;
}
如何使用
利用命令生成对应的代码文件后,使用方法如下:
import (
"xxxx/proto"
"google.golang.org/protobuf/types/known/timestamppb"
"time"
)
func main() {
student := proto.Strudent{
Age: 18,
Name: "张三",
Current: timestamppb.New(time.Now()),
}
}