什么是 Protobuf?如何使用 Protobuf?

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_idcustomer_nameproducts 字段。
  • Product 是嵌套的消息类型,它包含了表示产品的字段:product_idproduct_nameprice
  • 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: 0Gender: 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 表示映射中键的类型,可以是基本数据类型(例如 int32string)或枚举类型。
value_type 表示映射中值的类型,可以是任何支持的数据类型(例如 int32string、消息类型)。
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()),
	}
}
  • gRpc
    11 引用 • 9 回帖 • 70 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    497 引用 • 1388 回帖 • 277 关注
  • Protobuf
    9 引用 • 2 回帖
1 操作
godqi 在 2024-07-13 12:53:11 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • xqh042 1 评论

    fixtypo:

    「Protocol Buffers 基本类型和默认值」 标题下第一个表格的 fixed32 fixed64 的描述应该是无符号整数。

    感谢!
    godqi