protoBuf知识点

protoBuf

概述

Protobuf 是一种由 Google 开发的二进制序列化格式和相关的技术,它用于高效地序列化和反序列化结构化数据,通常用于网络通信、数据存储等场景

为什么使用

在分布式系统、RPC(Remote Procedure Call)框架和数据存储中得到了广泛应用,它提供了一种高效、简洁和可扩展的方式来序列化和交换数据

优点

  • 高效性:Protobuf 序列化后的二进制数据通常比其他序列化格式(比如超级常用的JSON)更小,并且序列化和反序列化的速度更快,这对于性能敏感的应用非常有益。

  • 简洁性:Protobuf 使用一种定义消息格式的语法,它允许定义字段类型、顺序和规则(消息结构更加清晰和简洁)

  • 版本兼容性:Protobuf 支持向前和向后兼容的版本控制,使得在消息格式发生变化时可以更容易地处理不同版本的通信。

  • 语言无关性:Protobuf 定义的消息格式可以在多种编程语言中使用,这有助于跨语言的通信和数据交换(截至本文发布目前官方支持的有C++/C#/Dart/Go/Java/Kotlin/python)

  • 自动生成代码:Protobuf 通常与相应的工具一起使用,可以自动生成代码,包括序列化/反序列化代码和相关的类(减少了手动编写代码的工作量,提高效率

protoBuf数据要素

  1. syntax:protobuf版本。一般放在.proto文件的第一行。比如proto3
  2. message:包含message名称和消息内字段的定义
  3. 字段:标签(可选) + 类型 + 名称 + 值
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保留作实现用

标签

  1. optional:表示消息可包含也可不包含该字段
  2. required:消息必须包含该字段
  3. repeated:该字段可以重复多次,用于表示数组或列表。每个元素都必须属于指定的数据类型。可以包含零个或多个值
  4. extensions:在不破坏现有消息格式的情况下添加新字段
int32 custom_field = 100;
  1. default:指定字段的默认值。如果消息中未设置该字段的值,将使用默认值
int32 age = 2 [default = 18];
  1. packed:指示重复字段是否应该使用编码方式进行紧凑打包,以减小序列化后的消息大小。通常用于重复的数值类型字段
repeated int32 numbers = 1 [packed = true];
  1. oneof:定义一组字段,这些字段中只能有一个实际存在
oneof contact_info { string email = 1; string phone = 2; string address = 3; }

8.deprecated:表示该字段已经弃用

9.reserved : 这个关键字用于在消息定义中指定一组字段号,以防止将来的版本中使用这些字段号。当消息被序列化和反序列化时,这些字段号将被跳过

字段类型

在protoBuf中,字段类型可以是一些标量、枚举类型,还可以是复合类型

标量

以Java为例

Protocol Buffers 类型Java 类型
doubledouble
floatfloat
int32int
int64long
uint32int
uint64long
sint32int
sint64long
fixed32int
fixed64long
sfixed32int
sfixed64long
boolboolean
stringString
bytesByteString
enumEnum
枚举
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
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值