原来可以这么使用 Protobuf

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 原来可以这么使用 Protobuf

1. Protobuf 简介

1.1 Protobuf 是什么

Protocol Buffers (简称 Protobuf )是 Google 公司开源的一种轻便高效的结构化数据存储格式,以及用于序列化和反序列化结构化数据的代码生成器。它可以用于通讯协议和数据存储等领域。

Protobuf 是以 .proto 文件形式定义结构化数据的方式和格式。

并且通过代码生成器生成各平台(Java、C++、Python、Go 等)的数据访问类,这些生成的类可以用来在对应的语言中解析、序列化 Protobuf 数据。

1.2 Protobuf 的优点

Protobuf 作为一种数据格式和工具,有以下优点:

(1)性能高,序列化和反序列化速度很快

Protobuf 采用二进制格式存储数据,相比 XML 和 JSON 格式,可以大幅减少数据体积, serialization 和 deserialization 的性能也更优。这对于高性能场景非常有利。

(2)跨平台,多语言支持广泛

Protobuf 提供了标准的 .proto 文件格式和数据描述语法,然后可以通过 protoc 工具,自动生成各主流语言的数据访问类,如 Java、C++、Python、Go 等。

这保证了在不同平台和不同语言 scenarios 下,可以解析和验证一致的 Protobuf 数据。

(3)定义结构化数据格式,方便维护升级

通过 .proto 文件定义数据格式,可以清晰界定不同版本数据格式的兼容关系,格式修改后也方便使用旧格式数据。

(4)数据体积小,便于存储和传输

相比 XML、JSON,Protobuf 的二进制编码可以大幅减小数据体积,节省存储和网络传输成本。

(5)扩展性好,灵活支持新增字段

通过定义可选和 Required 字段,可以轻松添加和删除消息中的字段,而不影响已有字段的序号,便于数据格式的扩展和演进。


 

2. Go 语言中使用 Protobuf

2.1 在 Go 语言中安装 Protobuf 库

在 Go 语言中使用 Protobuf 主要依赖 google 开源的 golang/protobuf 库,使用以下命令安装:


go get -u github.com/golang/protobuf/proto

安装完成后,可以在代码中 import 此库:


import "github.com/golang/protobuf/proto"

2.2 使用 protoc 编译.proto 文件

编写 .proto 文件后,需要使用 protoc 命令生成 Go 代码,例如:


protoc --go_out=. message.proto

这会根据 message.proto 中的消息定义,生成 Go 语言版本的访问类,存放在 message.pb.go 文件中。

2.3 Protobuf 消息的编码和解码

golang/protobuf 库中主要包含下面两个函数,用来序列化和反序列化 Protobuf 消息:


func Marshal(pb Message) ([]byte, error)func Unmarshal(buf []byte, pb Message) error

其中 Message 是一个满足 protobuf.Message 接口的 Protobuf 消息对象,可以是通过 .proto 生成的 pb.go 文件中定义的类型,也可以是动态消息。

这两个函数可以方便的在任意 Go 类型与 Protobuf 二进制格式之间进行转换。

2.4 Protobuf 服务的定义

除了用于数据存储、网络通信外,Protobuf 也可以用来定义服务接口(RPC 服务)。语法如下:


service SearchService {  rpc Search (SearchRequest) returns (SearchResponse);}
message SearchRequest {  string query = 1;   int32 page_number = 2;    int32 result_per_page = 3;}  message SearchResponse {  repeated Result results = 1;}
message Result {  string url = 1;    string title = 2;    repeated string snippets = 3;}

这样就定义了一个 RPC 服务 interface,包含一个 Search 方法。然后客户端和服务器端通过实现这个 interface,来发送、处理服务请求和响应。

服务端需要实现服务接口定义的方法,客户端需要调用这个接口方法,传递请求参数,获取响应结果。


 

3. Protobuf 消息的定义

通过 .proto 文件, 可定义 Protobuf 中的消息结构。Protobuf 消息由一系列字段组成,使用 message 定义,每个消息可包含多种类型的字段。

3.1 消息类型

Protobuf 支持标量类型、复合类型的消息定义。

量类型: 包括整型、浮点型、布尔型、字符串等;

复合类型: 主要是其他消息类型,一个消息字段可以引用其他消息类型。

3.2 标量类型

语法格式如下:


[修饰符] 类型名 字段名 = 字段号;

常用标量类型和修饰符总结如下:

int32,int64 - 有符号整型

uint32,uint64 - 无符号整型

bool - 布尔类型

string - 字符串类型

bytes - 字节数组

float,double - 浮点类型

repeated - 重复类型,表示数组

required - 必填字段

optional - 可选字段,默认值

示例:


message Person {  required string name = 1;  required int32 age = 2;    optional string email = 3;}

这定义了一个 Person 消息,包含必填的 name、age 字段和可选的 email 字段。

3.3 定义 request 和 response

可定义一对请求和响应消息,用于 RPC 服务接口的输入和输出参数。

语法结构如下:


// SearchRequest请求消息message SearchRequest {  required string query = 1;  optional int32 page = 2;    ...}
// SearchResponse响应消息  message SearchResponse {  repeated Result results = 1;  optional int32 total_results = 2;}

这样通过一对请求响应消息消息,定义了服务接口的入参和返回值格式。

3.4 import 公共 proto 文件

为了重用消息定义和其他 .proto 文件的内容,可以用 import 语句导入其他 .proto 文件。

例如:


import "other/other.proto";

这样就可以直接引用 other.proto 中定义的消息、枚举等。

3.5 使用 options 设置项

Protobuf 支持自定义 options 字段,对消息、枚举进行注解或设置生成参数:


message Foo {  optional string text = 1 [(custom_option) = "hello world"]; }

这为 text 字段添加一个自定义 option 注解。


 

4. Go 语言 Protobuf 实践

下面以一个完整的例子,演示下 Go 语言中使用 Protobuf 的整个流程。

4.1 定义 Protobuf 消息

编写一个 person.proto 文件,定义 Person 消息格式:


syntax = "proto3";
package tutorial;
message Person {  string name = 1;  int32 age = 2;   string email = 3;}

4.2 生成 Go 代码

然后使用 protoc 命令根据 person.proto 生成 Go 语言代码:


protoc --go_out=. person.proto

这会生成一个 person.pb.go 文件。

4.3 发送、接收 Protobuf 消息

有了生成的 Go 访问类, 就可很方便的在 Go 代码中处理 Person 消息。

例如序列化和反序列化:


package main
import (    "log"    "github.com/golang/protobuf/proto"    "path/to/personpb" // Import the generated personpb package)
func main() {    p := &personpb.Person{        Name:  "John Doe",        Age:   30,        Email: "john@email.com",    }
    data, err := proto.Marshal(p)    if err != nil {        log.Fatal("marshaling error: ", err)    }
    // Handle the marshaled data, for example, print it    log.Printf("Marshaled data: %v", data)
    // If you want to do something with the marshaled data, you can use it here}

4.4 Protobuf 服务端和客户端

可利用 Protobuf 来定义服务接口,下面演示服务器和客户端的实现。

person.proto 中定义服务:


service PersonService {  rpc GetPerson(GetPersonRequest) returns (Person) {}}
message GetPersonRequest {  string name = 1;}

这定义了一个 PersonService,包含获取 Person 的 GetPerson 方法。

在服务器代码中实现这个接口:


type server struct{}
func (s *server) GetPerson(ctx context.Context, req *GetPersonRequest) (*Person, error) {  // 从数据库中获取Person对象并返回}
func main() {  lis, err := net.Listen("tcp", ":50051")  srv := grpc.NewServer()  pb.RegisterPersonServiceServer(srv, &server{})   srv.Serve(lis)}

在客户端, 可调用这个接口:


conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())client := pb.NewPersonServiceClient(conn)
resp, err := client.GetPerson(context.Background(), &req)

这样通过 gRPC 框架,就可以访问服务器定义的 Protobuf 服务。

4.5 与 gRPC 集成

Protobuf 定义的消息和服务可以很容易的在 gRPC 框架中使用,gRPC 正是通过 Protobuf 接口定义实现服务通信的。

服务器端实现 Protobuf 接口,客户端调用接口,二者通过 gRPC 通讯。


 

5. Protobuf 使用注意事项和经验

5.1 版本控制

为了兼容旧版本,在修改消息定义时,应该谨慎地创建新字段而不是删除旧字段。

5.2 向后兼容

对于 int32、uint32、int64、uint64、bool、string、bytes 字段,新代码可以读写旧消息,前向后兼容性是没有问题的。

对于 repeated 字段,删除或者顺序改变字段号,会造成不兼容。

新增 optional 或 repeated 字段,前向后兼容性是没有问题。但是新增 required 字段则会造成解析问题。

所以新增字段时,应使用 optional 而不是 required。

5.3 包含大数据量字段

由于 Protobuf 是二进制编码的,如果有字段包含非常大的数据(如图片、视频),会大幅增加消息大小。

这时可以考虑通过指针引用独立文件的形式,避免消息体积过大。


 

6. 总结

6.1 Protobuf 优缺点

相比 XML 和 JSON 数据格式, Protobuf 作为一种高效的结构化数据存储和交换格式,具有以下优点:

编码效率高,序列化后数据体积小

解析速度快

支持数据格式升级与兼容

支持定义服务接口

跨平台跨语言,通过编译支持各平台访问

当然也存在一些限制,比如不适合处理频繁修改的数据格式,不支持数据查询等。以 Protobuf 在很多性能敏感、跨平台的场景下,可以发挥很好的作用。

6.2 在 Go 语言项目中的作用

在 Go 语言中,Protobuf 可以用于:

定义项目中的数据结构体

网络服务的请求响应参数和结果

RPC 服务接口定义

数据存储格式定义

通过 Protobuf 接口定义,可以实现服务端和客户端的松耦合。

并且利用 Protobuf 接口语言无关性,可以支持多语言访问后端 Go 服务,实现更好的语言融合。

目录
相关文章
|
6月前
|
JSON Java 数据格式
protobuf 使用和介绍
protobuf 使用和介绍
80 0
|
4月前
|
XML JSON Java
GRPC与 ProtoBuf 的理解与总结
GRPC与 ProtoBuf 的理解与总结
157 0
|
5月前
|
XML 存储 编译器
Protobuf 详解
Protobuf 详解
|
6月前
|
XML 存储 JSON
一文简单聊聊protobuf
一文简单聊聊protobuf
|
JavaScript Java PHP
Protobuf 3.3 使用总结
Protobuf 3.3 使用总结
171 0
|
XML 存储 Java
Protobuf了解一下?
Protobuf了解一下?
103 0
|
编译器
ProtoBuf的安装
ProtoBuf的安装
149 0
|
Java Android开发
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 使用文档 | 创建 Protobuf 源文件 | Protobuf 语法 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 使用文档 | 创建 Protobuf 源文件 | Protobuf 语法 )
348 0
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 使用文档 | 创建 Protobuf 源文件 | Protobuf 语法 )
|
Java Android开发
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 序列化 | Protobuf 反序列化 )
【Android Protobuf 序列化】Protobuf 使用 ( Protobuf 序列化 | Protobuf 反序列化 )
397 0