protoBuf
概述
Protobuf 是一种由 Google 开发的二进制序列化格式和相关的技术,它用于高效地序列化和反序列化结构化数据,通常用于网络通信、数据存储等场景
为什么使用
在分布式系统、RPC(Remote Procedure Call)框架和数据存储中得到了广泛应用,它提供了一种高效、简洁和可扩展的方式来序列化和交换数据
优点
-
高效性:Protobuf 序列化后的二进制数据通常比其他序列化格式(比如超级常用的JSON)更小,并且序列化和反序列化的速度更快,这对于性能敏感的应用非常有益。
-
简洁性:Protobuf 使用一种定义消息格式的语法,它允许定义字段类型、顺序和规则(消息结构更加清晰和简洁)
-
版本兼容性:Protobuf 支持向前和向后兼容的版本控制,使得在消息格式发生变化时可以更容易地处理不同版本的通信。
-
语言无关性:Protobuf 定义的消息格式可以在多种编程语言中使用,这有助于跨语言的通信和数据交换(截至本文发布目前官方支持的有C++/C#/Dart/Go/Java/Kotlin/python)
-
自动生成代码:Protobuf 通常与相应的工具一起使用,可以自动生成代码,包括序列化/反序列化代码和相关的类(减少了手动编写代码的工作量,提高效率)
protoBuf数据要素
syntax
:protobuf版本。一般放在.proto
文件的第一行。比如proto3message
:包含message名称和消息内字段的定义字段
:标签(可选) + 类型 + 名称 + 值
message SomeMsg {
reserved 10, 13 to 20;
reserved "resvItemName1";
optional uint32 someItem1 = 1;
optional SomeEnum someItem2 = 2;
repeated string someItem3 = 128 [packed = true];
}
字段编号
1.每个字段的编号在所属的message内是唯一的
2.编号值在1~536870911(2^29 − 1)之内(尽量小)
3不能使用的保留编号,19000~19999被protobuf保留作实现用
标签
optional
:表示消息可包含也可不包含该字段required
:消息必须包含该字段repeated
:该字段可以重复多次,用于表示数组或列表。每个元素都必须属于指定的数据类型。可以包含零个或多个值extensions
:在不破坏现有消息格式的情况下添加新字段
int32 custom_field = 100;
default
:指定字段的默认值。如果消息中未设置该字段的值,将使用默认值
int32 age = 2 [default = 18];
packed
:指示重复字段是否应该使用编码方式进行紧凑打包,以减小序列化后的消息大小。通常用于重复的数值类型字段
repeated int32 numbers = 1 [packed = true];
oneof
:定义一组字段,这些字段中只能有一个实际存在
oneof contact_info { string email = 1; string phone = 2; string address = 3; }
8.deprecated
:表示该字段已经弃用
9.reserved : 这个关键字用于在消息定义中指定一组字段号,以防止将来的版本中使用这些字段号。当消息被序列化和反序列化时,这些字段号将被跳过
字段类型
在protoBuf中,字段类型可以是一些标量、枚举类型,还可以是复合类型
标量
以Java为例
Protocol Buffers 类型 | Java 类型 |
---|---|
double | double |
float | float |
int32 | int |
int64 | long |
uint32 | int |
uint64 | long |
sint32 | int |
sint64 | long |
fixed32 | int |
fixed64 | long |
sfixed32 | int |
sfixed64 | long |
bool | boolean |
string | String |
bytes | ByteString |
enum | Enum |
枚举
enum SomeEnum {
someValue1 = 0;
someValue2 = 1;
someValue3 = 2;
}
复合类型
和高级语言中的嵌套结构很像
message OuterMessage {
// 其他字段的定义
message InnerMessage {
// 内部消息字段的定义
}
enum InnerEnum {
// 内部枚举值的定义
}
// 可以继续定义其他的内部类型
}
map
在protoBuf里,map也对应一个映射,一条map类型的字段这么定义:map<key_type, value_type> field_name = field_number;
key_type 是键的数据类型,可以是整数类型、字符串类型等。
value_type 是值的数据类型,可以是任何支持的数据类型,包括消息类型。
field_name 是字段的名称。
field_number 是字段的编号,用于在二进制编码中标识该字段。
syntax = "proto3";
message AddressBook {
string owner_name = 1;
// 定义一个 map 类型字段,表示联系人列表,键是字符串类型,值是 Contact 消息类型
map<string, Contact> contacts = 2;
}
message Contact {
string name = 1;
string email = 2;
}
// .cpp
AddressBook addressBook = AddressBook.newBuilder()
.setOwnerName("John Doe")
.putContacts("Alice", Contact.newBuilder()
.setName("Alice")
.setEmail("alice@example.com")
.build())
.putContacts("Bob", Contact.newBuilder()
.setName("Bob")
.setEmail("bob@example.com")
.build())
.build();
group
这其实是一个protobuf3 废弃的类型。将多个字段组织在一起,它在proto2版本中引入,并被设计为一种消息组织机制。每个组中的字段都被分配一个唯一的字段编号,并且可以将多个字段组合在一起,以便在解析时更容易处理。类似于之前说的复合类型
举个例子:MyMessage 包含一个名为 group_field 的字段,其类型是 MyGroup。MyGroup 包含了两个字段 field1 和 field2。使用group可以将这两个字段组织在一起,以便更容易地一起处理
syntax = "proto2";
message MyMessage {
message MyGroup {
required int32 field1 = 1;
required string field2 = 2;
}
optional MyGroup group_field = 3;
}
// 在protobuf3中,field1 和 field2 都是独立的字段,不再需要group来组织,直接这么写:
syntax = "proto3";
message MyMessage {
int32 field1 = 1;
string field2 = 2;
}
导入其他消息类型
语法:import "other.proto";
other.proto 是要导入的其他 .proto 文件的名称(相对于当前文件的路径)
编码解码
编码
编码有这么几个要点:
字段值:字段的值是消息中的实际数据。不同的数据类型使用不同的编码方式来表示字段的值。
Varint 编码:对于整数类型(例如 int32、int64、uint32、uint64、sint32 和 sint64),protobuf 使用Base 128 Varint编码来表示字段的值,可以根据值的大小选择使用不同的字节数来表示整数。
长度-值编码:对于字符串、字节数组和消息类型字段,protobuf 使用长度-值编码方式。首先,编码字段的长度,然后编码字段的值。可以在不解码字段的情况下跳过不感兴趣的字段。
重复字段编码:对于重复字段(repeated),多个值按顺序编码,形成一个列表。在编码中,首先编码列表的长度,然后编码每个元素的值。
解码
导入消息类型定义:在解码之前,需要导入用于定义消息类型的 .proto 文件或已编译的消息类型定义。确保解码器能够理解二进制数据的结构。
创建消息解码器:需要创建一个用于解码的消息对象,该对象对应于待解码的消息类型。
使用解码器解码数据:将二进制数据传递给消息解码器,以便将其还原为消息对象。解码器将根据字段的规则和类型来解析二进制数据。
proto生成文件
不同语言生成文件不同
c++ | .h 和.cc文件 |
---|---|
Java | .java 文件,以及用于创建消息类实例的特殊 Builder 类 |
Kotlin | .kt文件,包含用于简化创建消息实例的 DSL |
参考 | |
https://blog.csdn.net/weixin_43395063/article/details/132791697 |