为了账号安全,请及时绑定邮箱和手机立即绑定

Protobuf协议入门教程:轻松掌握数据序列化

标签:
数据结构 API
概述

Protobuf协议是由Google开发的一种高效、语言无关、平台无关的数据序列化格式,它允许结构化数据的高效序列化和反序列化。通过使用简单的文本格式来描述数据结构定义,Protobuf可以生成不同语言的序列化程序,从而轻松地将结构化数据序列化为二进制格式或从二进制格式反序列化。Protobuf协议具有高效性、语言无关性、平台无关性和灵活扩展性等优势,广泛应用于网络通信、数据存储、数据交换和日志记录等领域。

Protobuf协议简介
什么是Protobuf协议

协议缓冲区协议(Protocol Buffers,简称Protobuf)是由Google开发的一种数据序列化格式,它允许结构化数据的高效、语言无关、平台无关的序列化。Protobuf通过使用一个简单的文本格式来描述数据结构定义,然后使用这些定义来生成不同语言的序列化程序,从而能够轻松地将结构化数据序列化为二进制格式,或者从二进制格式反序列化。

Protobuf协议的优势和应用场景

优势

  1. 高效性:相比XML和JSON等序列化格式,Protobuf在序列化和反序列化过程中占用更低的网络带宽,更节省存储空间。其序列化格式是二进制的,比文本格式更紧凑。
  2. 语言无关性:Protobuf支持多种编程语言,包括C++、Java、Python等。这意味着你可以使用任何一种支持Protobuf的语言来读取和写入由其他语言生成的数据。
  3. 平台无关性:Protobuf可以在不同的操作系统上运行,包括Windows、Linux和macOS等。
  4. 灵活扩展性:增加或删除消息字段时,不会导致旧版本无法读取新版本的问题,只需要在定义文件中添加或删除对应字段即可。

应用场景

Protobuf通常用于以下场景:

  1. 网络通信:在客户端与服务器之间的通信中,Protobuf可以有效地序列化和反序列化数据,减少网络传输的数据量。
  2. 数据存储:大多数数据库的存储格式都是二进制的,使用Protobuf可以有效地将结构化数据序列化为二进制格式,便于存储在数据库中。
  3. 数据交换:不同系统之间数据交换时,可以用Protobuf格式来传输数据,保证数据的正确解码和解析。
  4. 日志记录:日志记录系统可以使用Protobuf来序列化日志数据,便于后续的解析和处理。
Protobuf协议的基本概念
消息定义和.proto文件

在Protobuf中,消息的定义是通过一个特殊的.proto文件来描述的。这些文件遵循一种特殊的语法,用于定义消息的结构和字段。每一行代码代表一个字段或一个消息类型。以下是一个简单的.proto文件示例:

syntax = "proto3";
package tutorial;

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

语法解析

  • syntax = "proto3";:指定使用的Protobuf版本。
  • package tutorial;:定义包名,防止名称冲突。
  • message Person { ... }:定义一个消息类型,名为Person
  • string name = 1;:定义一个字符串类型的字段,命名为name,编号为1。
  • enum PhoneType { ... }:定义一个枚举类型,包含三个枚举值。
  • message PhoneNumber { ... }:定义一个嵌套的消息类型,用于存放电话号码。
  • repeated PhoneNumber phones = 4;:定义一个重复的消息类型字段,编号为4。
字段类型和选项

基本字段类型

  1. int32:有符号整数,范围是-2^31到2^31-1。
  2. uint32:无符号整数,范围是0到2^32-1。
  3. int64:有符号整数,范围是-2^63到2^63-1。
  4. uint64:无符号整数,范围是0到2^64-1。
  5. float:32位浮点数。
  6. double:64位浮点数。
  7. bool:布尔值,取值为truefalse
  8. string:字符串,使用UTF-8编码。
  9. bytes:字节序列,可以用于存储二进制数据。

示例

以下是一个简单的.proto文件,定义了一个包含三个字段的消息类型:

syntax = "proto3";

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;
}

在这个例子中,name字段是一个字符串类型,id字段是一个整数类型,is_active字段是一个布尔类型。

嵌套消息类型

除了基本字段类型,还可以定义嵌套的消息类型,这些类型可以作为字段的值。例如,在上文的Person消息中,定义了一个嵌套的消息类型PhoneNumber,用于存放电话号码。

示例

以下是一个包含嵌套消息类型的.proto文件:

syntax = "proto3";

package tutorial;

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;

  message NestedMessage {
    string nested_name = 1;
  }
}

在这个例子中,Example消息包含了一个嵌套的消息类型NestedMessage

重复字段

可以通过关键字repeated来定义一个包含相同类型元素的列表。例如,repeated PhoneNumber phones = 4;定义了一个电话号码列表。

示例

以下是一个包含重复字段的.proto文件:

syntax = "proto3";

message Example {
  repeated int32 numbers = 1;
}

在这个例子中,numbers字段是一个重复的整数列表。

标签和字段类型

每个字段都必须有一个唯一的标签,标签的取值范围是1到1048576。标签的编号是标识字段的唯一标识符,用于在消息的二进制格式中表示字段。

默认值

每个字段都有默认值。例如,string类型的默认值是空字符串,int32类型的默认值是0。如果在消息中没有设置某个字段,那么在序列化和反序列化时将使用默认值。

如何安装和配置Protobuf协议
下载和安装Protobuf编译器

Protobuf的编译器叫做protoc,用于将.proto文件转换为指定语言的代码。以下是安装过程:

  1. 下载Protobuf:访问Protobuf的GitHub仓库,根据操作系统下载对应的安装包。
  2. 安装:对于Linux和macOS用户,可以使用包管理器安装Protobuf。例如,使用Homebrew安装Protobuf:
brew install protobuf

对于Windows用户,可以从GitHub仓库下载安装包,解压后将bin目录添加到系统PATH环境变量中。

配置环境变量

安装完Protobuf编译器之后,需要配置环境变量以便在命令行中使用。

Linux和macOS

export PATH=$PATH:/usr/local/bin

Windows

  1. 打开“系统属性”对话框(右键点击“此电脑”或“计算机”,选择“属性”->“高级系统设置”->“环境变量”)。
  2. 在“系统变量”区域,找到“Path”变量,点击“编辑”。
  3. 在弹出的对话框中,点击“新建”,添加Protobuf编译器的bin目录路径。
  4. 点击“确定”关闭所有对话框。

配置完成后,可以使用命令protoc --version来验证安装是否成功。

Protobuf协议的使用示例
编写.proto文件定义消息结构

首先,需要定义一个.proto文件来描述消息的结构。以下是一个简单的.proto文件,定义了一个包含三个字段的消息类型:

syntax = "proto3";

package example;

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;
}
使用编译器生成语言特定代码

定义好.proto文件后,需要使用protoc编译器将其转换为指定语言的代码。例如,使用Java作为示例语言,可以使用以下命令生成Java代码:

protoc -I=. --java_out=. example.proto

这将生成一个Java文件,通常命名为Example.java,其中包含了对Example消息类型的定义。

示例

以下是一个使用生成的Java代码进行编码和解码的示例:

编码示例

import com.example.example.Example;

public class ProtobufExample {
  public static void main(String[] args) throws Exception {
    // 创建一个Example对象
    Example example = Example.newBuilder()
      .setName("John Doe")
      .setId(123)
      .setIsActive(true)
      .build();

    // 序列化为二进制格式
    byte[] serialized = example.toByteArray();
    System.out.println("Serialized: " + new String(serialized));
  }
}

解码示例

import com.example.example.Example;

public class ProtobufExample {
  public static void main(String[] args) throws Exception {
    // 解析二进制数据为Example对象
    Example example = Example.parseFrom(serializedData);

    // 输出消息内容
    System.out.println("Name: " + example.getName());
    System.out.println("ID: " + example.getId());
    System.out.println("Active: " + example.getIsActive());
  }
}
如何处理字段的增加和删除

增加字段

增加新的字段时,只需在.proto文件中添加相应的字段定义即可。例如:

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;
  string new_field = 4;
}

字段的编号必须是递增的,否则会报错。

示例

以下是一个增加字段的示例:

syntax = "proto3";

package example;

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;
  string new_field = 4;
}

删除字段

删除字段时,只需从.proto文件中删除相应的字段定义即可。例如:

message Example {
  string name = 1;
  int32 id = 2;
  // 删除了is_active字段
}

如果删除了某些旧版本中已存在的字段,但旧版本仍然依赖这些字段,那么当旧版本尝试解析新版本的消息时,可能会出现问题。为了解决这个问题,可以将旧版本中的字段标记为deprecated,在.proto文件中使用[deprecated=true]选项。

示例

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3 [deprecated = true];

  // 新增字段
  string new_field = 4;
}
常见问题解答
解决编译错误的常见方法
  1. 检查.proto文件的语法:确保.proto文件遵循正确的语法。常见的错误包括未正确闭合语句或拼写错误。
  2. 检查包名和消息类型:确保在.proto文件中定义的包名和消息类型在生成代码时没有拼写错误。
  3. 检查编译命令:确保使用正确的编译命令。例如,protoc -I=. --java_out=. example.proto
如何处理字段的增加和删除

增加字段

增加新的字段时,只需在.proto文件中添加相应的字段定义即可。例如:

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3;
  string new_field = 4;
}

字段的编号必须是递增的,否则会报错。

删除字段

删除字段时,只需从.proto文件中删除相应的字段定义即可。例如:

message Example {
  string name = 1;
  int32 id = 2;
  // 删除了is_active字段
}

如果删除了某些旧版本中已存在的字段,但旧版本仍然依赖这些字段,那么当旧版本尝试解析新版本的消息时,可能会出现问题。为了解决这个问题,可以将旧版本中的字段标记为deprecated,在.proto文件中使用[deprecated=true]选项。

示例

message Example {
  string name = 1;
  int32 id = 2;
  bool is_active = 3 [deprecated = true];

  // 新增字段
  string new_field = 4;
}
小结与进阶学习资源
Protobuf协议的优缺点总结

优点

  1. 高效性:相比XML和JSON等序列化格式,Protobuf在序列化和反序列化过程中占用更低的网络带宽,更节省存储空间。
  2. 语言无关性:Protobuf支持多种编程语言,包括C++、Java、Python等。
  3. 平台无关性:Protobuf可以在不同的操作系统上运行。
  4. 灵活扩展性:增加或删除消息字段时,不会导致旧版本无法读取新版本的问题。

缺点

  1. 不支持嵌套的默认值:如果一个消息类型中包含了另一个消息类型,那么在序列化和反序列化时,嵌套的消息类型中的默认值不会被正确处理。
  2. 不支持循环引用:如果一个消息类型中引用了另一个消息类型,而另一个消息类型中又引用了第一个消息类型,那么这种循环引用会导致序列化和反序列化失败。
推荐的进一步学习资源
  • 官方文档:Protobuf的官方文档提供了详细的教程和示例,帮助你更深入地了解Protobuf。
  • 慕课网:慕课网提供了很多关于Protobuf的学习课程,包括实际项目案例和实战演练。
  • GitHub仓库:访问Protobuf的GitHub仓库,可以查看更多的示例代码和文档。
  • Stack Overflow:在Stack Overflow上搜索Protobuf相关的问题,可以找到很多有用的解答和经验分享。
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消