Google 序列化神器 Protocol Buffer 学习指南

2024-06-17 22:11:19 浏览数 (1)

简介

在现代软件开发中,数据的高效传输和存储是一个关键问题。Google 开发的 Protocol Buffer(简称 Protobuf)作为一种语言中立、平台无关、可扩展的机制,用于高效地序列化结构化数据。它比 XML 或 JSON 更加紧凑和高效,非常适合需要高性能和小体积的场景。

本文将详细介绍 Protobuf 的概念、使用方法以及一些高级特性,帮助读者快速掌握这一强大的工具。

为什么选择 Protobuf

  1. 性能高:Protobuf 使用二进制格式进行数据序列化,解析速度比 XML 和 JSON 快很多。
  2. 数据体积小:相比 XML 和 JSON,Protobuf 序列化后的数据体积更小,适合带宽受限的场景。
  3. 跨语言支持:Protobuf 支持多种编程语言,包括但不限于 C , Java, Python, Go 等。
  4. 向后兼容:Protobuf 允许你在不破坏现有数据格式的前提下对消息进行扩展,非常适合需要频繁迭代和升级的系统。

安装与环境配置

要开始使用 Protobuf,需要安装 Protocol Buffers 编译器 protoc。以下是一些主要平台的安装方法:

在 macOS 上

使用 Homebrew 安装:

代码语言:javascript复制
brew install protobuf
在 Ubuntu 上

使用 apt-get 安装:

代码语言:javascript复制
sudo apt-get install -y protobuf-compiler
在 Windows 上

下载预编译的二进制文件,并将其添加到系统路径中。下载地址:Protocol Buffers Releases

定义 Protocol Buffer 消息

使用 .proto 文件定义数据结构,以下是一个简单的例子:

代码语言:javascript复制
syntax = "proto3";

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

上述代码定义了一个 Person 消息,包含三个字段:nameidemailsyntax = "proto3"; 表示我们使用的是 Protobuf 的第三版语法。

编译 .proto 文件

编译 .proto 文件生成相应语言的代码。假设我们有一个 addressbook.proto 文件:

代码语言:javascript复制
protoc --java_out=. addressbook.proto

上述命令会在当前目录生成 Java 文件。如果你需要生成其他语言的代码,可以指定对应的参数,比如 --python_out=. 生成 Python 代码。

使用生成的代码

以 Java 为例,假设我们已经生成了 Person.java,可以使用如下代码进行数据序列化和反序列化:

序列化
代码语言:javascript复制
Person person = Person.newBuilder()
    .setName("John Doe")
    .setId(1234)
    .setEmail("john.doe@example.com")
    .build();

FileOutputStream output = new FileOutputStream("person.ser");
person.writeTo(output);
output.close();
反序列化
代码语言:javascript复制
FileInputStream input = new FileInputStream("person.ser");
Person person = Person.parseFrom(input);
input.close();

System.out.println("Name: "   person.getName());
System.out.println("ID: "   person.getId());
System.out.println("Email: "   person.getEmail());

Protobuf 高级特性

可选和重复字段

Protobuf 支持可选和重复字段,使用 optionalrepeated 关键字:

代码语言:javascript复制
message Contact {
  string name = 1;
  optional string email = 2;
  repeated string phone = 3;
}

optional 字段可以有也可以没有,repeated 字段则是一个数组,可以包含零个或多个值。

嵌套消息

Protobuf 允许在消息中嵌套其他消息:

代码语言:javascript复制
message AddressBook {
  repeated Person people = 1;
}

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
}
枚举类型

Protobuf 支持枚举类型,用于定义一组命名值:

代码语言:javascript复制
message Person {
  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

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

  string name = 1;
  int32 id = 2;
  string email = 3;
  repeated PhoneNumber phones = 4;
}
一对多关联

使用 map 类型可以定义一对多关联:

代码语言:javascript复制
message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
  map<string, string> attributes = 4;
}

Protobuf 与 gRPC

Protobuf 经常与 gRPC 一起使用,gRPC 是一个高性能、开源和通用的 RPC 框架,由 Google 开发。它使用 HTTP/2 作为传输协议,并使用 Protobuf 作为接口定义语言。

定义 gRPC 服务

.proto 文件中定义 gRPC 服务:

代码语言:javascript复制
syntax = "proto3";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
生成服务代码

使用 protoc 生成服务代码:

代码语言:javascript复制
protoc --java_out=. --grpc-java_out=. helloworld.proto
实现服务

以 Java 为例,实现 Greeter 服务:

代码语言:javascript复制
public class GreeterImpl extends GreeterGrpc.GreeterImplBase {
  @Override
  public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
    HelloReply reply = HelloReply.newBuilder().setMessage("Hello "   req.getName()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();
  }
}

Protobuf 的最佳实践

  1. 合理定义字段编号:字段编号一旦定义,尽量不要修改,以保持向后兼容。
  2. 使用默认值:Protobuf 的每个字段都有默认值,如字符串的默认值是空字符串,数值的默认值是零等。
  3. 避免重复字段编号:不同消息类型中的字段编号是独立的,但同一消息类型中的字段编号必须唯一。
  4. 利用 reserved 关键字:如果需要废弃某个字段编号或字段名称,可以使用 reserved 关键字: proto复制代码message Person { reserved 4, 5; reserved "old_field"; string name = 1; int32 id = 2; string email = 3; }

0 人点赞