详细了解 Protocol Buffers (protobuf)

引言

在分布式系统和微服务架构中,高效的数据序列化和反序列化是关键。虽然 JSON 和 XML 是广泛使用的数据格式,但它们并不总是最优的选择。Protocol Buffers(protobuf)是由 Google 开发的一种高效的序列化机制,它提供了一种更快速、更小的数据交换格式,并且通常用于rpc服务的通信中。

特点:

  1. 跨平台:支持多种编程语言,包括但不限于 C++, Java, Python, Go。

  2. 高效性:比 JSON 和 XML 更小的二进制格式,占用更少的网络带宽和存储空间。

  3. 可扩展性:支持向后和向前兼容,方便数据结构的升级。

protobuf 的基本使用

安装 protobuf

首先,我们需要安装 protobuf 编译器 protoc。以下是安装步骤:

  • macOS

    brew install protobuf
    
  • Ubuntu

    sudo apt-get install protobuf-compiler
    

定义 .proto 文件

使用 protobuf 的第一步是定义你的数据结构。我们通过 .proto 文件来描述这些结构。例如,我们定义一个简单的用户信息结构:

syntax = "proto3";//声明版本号
​
message User {
  string name = 1;//字段1
  int32 id = 2;
  string email = 3;
}

在这个定义中,syntax = "proto3" 表示我们使用的是 protobuf 的第三版语法。message则是类似于c++中的class声明,定义一个数据体。

编译 .proto 文件

接下来,我们需要使用 protoc 编译器将 .proto 文件编译成目标编程语言的代码。这里我们使用c++,可以使用以下命令:

protoc --cpp_out=. user.proto

这将生成两个文件:user.pb.huser.pb.cc,我们可以在 C++ 代码中直接使用。当我们编译proto文件后,里面的message声明的代码端,会自动被声明为同名的类,并且对其中的变量,采用默认的成员函数来进行操作。比如有一个string类型的name变量,则会默认生成获取name值的string name()成员函数和设置name值的 void set_name(string name)的成员函数。

在代码中使用

以下是一个简单的示例,展示如何在 C++ 中使用生成的 protobuf 代码:

#include <iostream>
#include <fstream>
#include "user.pb.h"
​
using namespace std;
​
int main() {
    // 创建一个 User 对象,这里的User对象来自user.pb.h文件,也就是编译生成后的文件
    User user;
    user.set_name("张三");
    user.set_id(123);
    user.set_email("2813348758@qq.com");
​
    // 序列化为二进制格式并写入文件
    fstream output("user_data.bin", ios::out | ios::trunc | ios::binary);
    if (!user.SerializeToOstream(&output)) //SerializeToOstream通过改函数将数据序列化,并将序列化后的数据传给传出参数,也就是这里的output
    {
        cerr << "Failed to write user data." << endl;
        return -1;
    }
    output.close();
​
    // 从文件中读取并反序列化
    User user_new;
    fstream input("user_data.bin", ios::in | ios::binary);
    if (!user_new.ParseFromIstream(&input)) //ParseFromIstream通过该函数进行反序列化,将传入参数进行反序列化赋值给User对象
    {
        cerr << "Failed to read user data." << endl;
        return -1;
    }
    input.close();
​
    // 输出反序列化后的数据
    cout << "Name: " << user_new.name() << ", ID: " << user_new.id() << ", Email: " << user_new.email() << endl;
    return 0;
}

在这个示例中,我们创建了一个 User 对象,并将其序列化为二进制数据,然后又将其反序列化为新的 User 对象。

两个字段声明:

repeated:

repeated 字段用于表示一个字段可以出现零次或多次,相当于一个数组或列表。这对于表示多个值的集合非常有用,例如一个人的多个电话号码。

optional:

在 proto3 中,所有字段默认都是可选的,即使不显式声明 optional。这意味着你可以不设置某个字段,它的默认值会被使用。

枚举enum的使用:

enum枚举类型允许你为一组常量值定义一个名称,提供了一种便捷的方式来表示一组预定义的可能值。枚举类型的使用可以提高代码的可读性和可维护性。使用enum关键字来声明,使用类似于c++中枚举类型的使用,不做过多介绍

定义rpc服务方法:

使用service 关键字用于定义远程过程调用 (RPC) 服务。一个 service 包含一组 RPC 方法,每个方法定义了请求消息和响应消息的类型。

举个栗子:

假设我们要定义一个用户管理服务,其中包括添加用户和获取用户信息的方法:

syntax = "proto3";
package user;
message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;
}
​
message AddPersonRequest    //添加用户功能的请求,添加一个person类
{
  Person person = 1;
}
​
message AddPersonResponse   //添加用户功能的响应,根据返回的success来判断请求是否成功
{
  bool success = 1;
}
​
message GetPersonRequest    //获取用户功能的请求,根据id获取用户信息
{
  int32 id = 1;
}
​
message GetPersonResponse   //获取用户功能的响应,对应请求,返回相应的person信息
{
  Person person = 1;
}
​
service UserService {
  rpc AddPerson(AddPersonRequest) returns (AddPersonResponse);
  rpc GetPerson(GetPersonRequest) returns (GetPersonResponse);
}

注意:这里使用rpc服务,则需要安装相应的grpc插件

sudo apt-get install -y protobuf-compiler
sudo apt-get install -y grpc

编译 .proto 文件生成 C++ 源文件:

protoc --grpc_out=. --plugin=protoc-gen-grpc=`which grpc_cpp_plugin` user.proto
protoc --cpp_out=. user.proto

这将生成以下文件:

  • user.pb.huser.pb.cc:包含消息类的定义和实现。

  • user.grpc.pb.huser.grpc.pb.cc:包含服务类的定义和实现。

protobuf 的优缺点

优点

  1. 高效:比 JSON 和 XML 更紧凑,占用更少的带宽和存储空间。

  2. 跨语言支持:支持多种编程语言,方便在不同系统之间进行数据交换。

  3. 版本兼容性:protobuf 的消息结构可以轻松地向后兼容和向前兼容。

缺点

  1. 可读性差:二进制格式不如 JSON 和 XML 可读,调试时需要借助工具。

结论

Protocol Buffers 是一种高效、跨平台的序列化工具,适用于需要高效数据传输的场景。虽然它的二进制格式在可读性上不如 JSON 和 XML,但它在性能和灵活性上具有明显优势。无论是大型分布式系统还是微服务架构,protobuf 都是一个值得考虑的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码商道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值