Protobuf:基本概念与使用流程

Protobuf:基本概念与使用流程


基本概念

在进行网络编程时,经常需要进行数据传输,只有双方主机都保证数据格式的一致性,才能保证数据被正常解析。这个过程称为序列化与反序列化,当前主流的标准有json、xml等,而protobuf就是其中一个数据格式的标准。

json和xml都是人类可视化的序列化形式,存储的都是字符串,哪怕没有程序解析都可以直接读取。

比如一个Person类,分别用json和xml序列化的结果:

json:

json 复制代码
{
  "name" : "John Doe",
  "age" : 30,
  "email" : "john.doe@example.com"
}

xml:

xml 复制代码
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person>
    <name>John Doe</name>
    <age>30</age>
    <email>john.doe@example.com</email>
</Person>

哪怕没有学习过相关语法,也可以很容易读取出序列化后的内容所包含的信息。

而protobuf存储的方式则是二进制形式,序列化后无法直接读取,只能看到乱码,必须由程序完成解析。相应的,protobuf的效率会比上面两者高很多,因为protobuf对数据的压缩效率极高,牺牲可视化换取高效,在一些需要高效传输数据的场景很有用。

一个序列化相关的协议,想要在计算机语言中使用,自然要配备相关的库,比如JavaScript原生对json的支持,C++通过jsoncpp库对json的支持。

protobuf由谷歌开发,谷歌也编写了相关的库,其可以支持C++、Java、Python、Go、C#、JavaScript、PHP等主流语言,本博客以C++讲解protobuf。


使用流程

.proto文件

就像C语言基于.c文件,C++基于.cpp文件,protobuf是基于.proto文件使用的。在.proto文件内部基于proto3语法编写文档,就可以自动生成其他语言的代码。

创建一个test.proto文件,在内部写以下内容:

cpp 复制代码
syntax = "proto3";
package test_pack;

message Person {
    string name = 1;
    int32 age = 2;
}

// 一条注释

.proto文件基本格式如下:

  1. 语法指定行

首行固定为语法指定行,用于指定protobuf的语法版本,格式:

cpp 复制代码
syntax = "版本号";

目前最新的版本为proto3,填入:

cpp 复制代码
syntax = "proto3";

注意一定要在首行,哪怕上面有空行也不行,否则无法编译。

  1. package命名空间

package是一个命名空间,可以避免命名冲突,类似于C++中的namespace或者Java中的package。这是一个可选项,如果不怕命名冲突,也可以不指定。

语法:

cpp 复制代码
package 命名空间;
  1. message 消息

message用于定义一个结构化的对象,其实就是一个class,内部可以定义成员。

成员字段的格式如下:

cpp 复制代码
类型 成员名 = 标签;

具体类型比较多,会有专门的博客讲解protobuf的类型。标签是protobuf压缩数据的重要方式,就是给每个变量指定一个编号,后续会专门讲解该内容。

标签范围: 1 , ( 2 29 − 1 ) 1, (2\^{29} - 1) 1,(229−1)

其中[19000, 19999]不可用,是保留的编号。

解析:

cpp 复制代码
message Person {
    string name = 1;
    int32 age = 2;
}
  • name:字符串类型,标签为1
  • age:32位整型,标签为2

学过任何一门面向对象语言,这些内容都很好理解。

  1. 注释

.proto文件中注释格式有两种:

cpp 复制代码
// 行注释

/* 块注释 */

另外的,注释不占行数,也就是说首行可以是注释,不会影响syntax指定的语法标准:

cpp 复制代码
// 注释...
// 注释...
// 注释...
syntax = "proto3";

以上写法是合法的,syntax前面可以有注释,但不能有空行或其他内容。


编译

编写好一个基本的.proto文件后,就可以对其进行编译,需要通过protoc指令:

bash 复制代码
protoc [--proto_path=improt路径] --cpp_out=目标路径 源路径.proto
  1. --proto_path:指定 .proto 文件的搜索路径

在.proto文件中,可以通过import导入其它的.proto文件,此时就需要通过--proto_path来指定其他文件的查找路径,否则无法找到文件。

  1. --cpp_out=目标路径

--cpp_out用于指定输出C++语言的代码,目标路径是生成的代码的位置。

其他语言的选项:

选项 描述
--cpp_out 指定生成 C++ 代码的目录
--java_out 指定生成 Java 代码的目录
--python_out 指定生成 Python 代码的目录
--csharp_out 指定生成 C# 代码的目录
--go_out 指定生成 Go 代码的目录
--js_out 指定生成 JavaScript 代码的目录
--objc_out 指定生成 Objective-C 代码的目录
--php_out 指定生成 PHP 代码的目录
--ruby_out 指定生成 Ruby 代码的目录
--grpc_out 指定生成 gRPC 服务端和客户端代码的目录
--grpc_java_out 指定生成 Java gRPC 代码的目录
--swift_out 指定生成 Swift 代码的目录
  1. 源文件路径

最后再指定xxx.proto源文件的路径。

如果是C++,编译后会产生如下文件:

至少产生了xxx.pb.cc和xxx.pb.h,这些就是生成的C++代码,后续可以直接使用内部的接口。


使用

在xxx.pb.h文件中至少可以找到以下内容:

cpp 复制代码
namespace test_pack {

class Person final : public ::google::protobuf::Message
/* @@protoc_insertion_point(class_definition:test_pack.Person) */ {
 public:
  inline Person() : Person(nullptr) {}
  ~Person() PROTOBUF_FINAL;
  // ...
  }
}

可以看到一个命名空间域namespace test_pack,这就是之前在.proto文件中写的package test_pack,最后转化为了C++的命名空间域。

message Person也转化为了class Person,其内部实现了大量接口,包含get,set等方法,以及序列化和反序列化接口。


运行机制

至此也可以看出protobuf具体是如何运行的了,如下图:

使用protobuf需要编写.proto文件,随后通过protoc编译器编译.proto文件,就可以得到对应语言的文件。在对应语言的文件中,会包含各类接口,最重要的就是序列化和反序列化。

在.proto文件中,会有很多和计算机语言具体对应的概念,比如message对应类,package对应命名空间域,或者其他语言的包。他们都会在编译器的作用下,自动转化成对应的语言。

最后只要往自己的业务代码中引入文件,比如#include "xxx.pb.h",或者其他语言的import等,就可以直接在业务代码中使用protobuf了。


相关推荐
我不会起名字3229 小时前
Redis 从 0 到 1(二十七):JSON 体积是 Protobuf 的 3.7 倍?3 种序列化的 Go 基准测试
redis·go·序列化·protobuf·messagepack
j7~7 天前
【ProtoBuf】(篇一)初识 ProtoBuf
运维·服务器·序列化·protobuf·protobuf的适用特点
我的div丢了肿么办9 天前
gin框架设置/获取cookie和多个二级域名共享cookie
后端·google
syagain_zsx13 天前
BRpc 搭建简单 rpc 服务器
服务器·c++·网络协议·rpc·protobuf·库
换元不配限14 天前
Android 接入 protobuf 实战:从 Gradle 配置到避开 R8 混淆坑
android·grpc·序列化·protobuf·protocolbuffers·二进制序列化·.proto
syagain_zsx15 天前
protobuf 实战:从 .proto 到 C++ 序列化的完整链路
c++·学习·程序人生·谷歌·protobuf
GlobalTradercrb16 天前
独立站SEO先写博客还是先优化产品页?看站点阶段
数据库·人工智能·google·pdf·go·seo
智码看视界16 天前
开源大模型追踪:TurboQuant量化指南:128K上下文不再爆显存,KV缓存3-bit免重训
google·开源大模型·llama.cpp·显存优化·长上下文推理·turboquant·本地部署 开源大模型
Frag0ut20 天前
获取Chrome官方安装包:离线版安装风险避坑指南
chrome·google·谷歌浏览器·安装包·chromesetup.exe·在线安装包·离线版
用户384816180183620 天前
写 Go 命令行小工具时我常用的几个习惯
google