当前位置: 首页 > news >正文

ProtoBuf使用手册(入门)

目录

  • ProtoBuffer使用入门
    • Protobuf概念
      • 一、Protobuf 的核心定位:为什么需要它?
      • 二、Protobuf 的核心工作原理
      • 三、Protobuf 的核心应用场景
      • 四、Protobuf 的关键概念补充
    • Protobuf (.proto) 文件语法规则
        • 0. 数据类型
          • (1)标量类型
          • (2)复合类型
          • (3)特殊类型
          • (4)通用注意事项
        • 1. 版本声明
        • 2. 包声明
        • 3. 消息定义(Message)
        • 4. 字段类型
        • 5. 服务定义(Service)
    • 生成代码的使用方法
      • 1. 编译 .proto 文件
      • 2. 核心类与函数
        • (1)对象创建与字段设置
        • (2)序列化与反序列化
        • (3)字段访问与判断
      • 3. 核心函数参数详解
    • 注意事项

ProtoBuffer使用入门

Protobuf概念

Protobuf(全称为 Protocol Buffers)是由 Google 开发的一种轻便、高效的结构化数据序列化协议,核心作用是实现数据的结构化存储与跨平台、跨语言的高效传输,本质是一套 “数据格式约定 + 编解码工具链”,解决了传统数据交换格式(如 XML、JSON)在 “体积” 和 “解析效率” 上的痛点。

一、Protobuf 的核心定位:为什么需要它?

在分布式系统(如微服务、跨语言通信)或数据存储场景中,需要将内存中的结构化数据(如 “用户信息”“订单数据”)转为可传输 / 存储的二进制格式,或从二进制格式恢复为内存数据 —— 这个过程就是 “序列化” 与 “反序列化”。

Protobuf 相比 XML、JSON 的核心优势:

特性ProtobufJSONXML
数据体积极小(二进制压缩,无冗余字符)较大(文本格式,含引号 / 逗号)最大(标签冗余多)
解析效率极高(预编译生成代码,直接操作内存)中等(需解析文本结构)较低(标签嵌套解析复杂)
跨语言支持原生支持(C++/Java/Python/Go 等)依赖第三方库依赖第三方库
类型安全性强类型(.proto 定义字段类型)弱类型(值类型需动态判断)弱类型(需解析标签类型)
兼容性(版本迭代)天然支持(字段编号不变即可兼容)需手动处理(如新增字段兼容)需手动处理(标签新增兼容)

二、Protobuf 的核心工作原理

Protobuf 的工作流程围绕 .proto 文件编解码工具链 展开,核心是 “先定义结构,再生成代码,最后编解码”:

  1. 定义数据结构(.proto 文件)通过专门的语法(如 proto3)在 .proto 文件中描述数据的结构(类似 “数据蓝图”),例如定义 “用户” 包含 “姓名、ID、爱好” 等字段:

    syntax = "proto3"; // 声明使用 proto3 语法
    package user;      // 避免命名冲突(生成代码时作为命名空间)message Person {   // 定义数据结构(类似类/结构体)string name = 1;  // 字段:类型 名称 = 字段编号(核心!用于编解码)int32 id = 2;     // 字段编号:1-15 占1字节,常用字段优先用repeated string hobbies = 3; // repeated:表示可重复(类似数组)
    }
    
  2. 编译生成代码(protoc 工具)使用 Google 提供的 protoc 编译器,根据 .proto 文件生成对应编程语言的 “编解码代码”(如 C++ 的 .h/.cc、Java 的 .java)。例:生成 C++ 代码的命令:

    protoc --cpp_out=./ ./person.proto  # --cpp_out:指定输出目录;后面跟 .proto 文件路径
    

    生成的代码中包含:

    • 对应 .protomessage 的类(如 C++ 中的 user::Person 类);
    • 字段的访问 / 修改函数(如 set_name()name()add_hobbies());
    • 序列化(转二进制)/ 反序列化(读二进制)函数(如 SerializeToString()ParseFromString())。
  3. 编解码与使用在业务代码中引入生成的代码,直接通过类的方法实现 “内存数据 ↔ 二进制数据” 的转换,无需手动处理编解码细节:

    • 序列化:将内存中的 Person 对象转为二进制字符串(可传输 / 存储);
    • 反序列化:将二进制字符串恢复为内存中的 Person 对象(可直接访问字段)。

三、Protobuf 的核心应用场景

Protobuf 因 “小体积、高效率、强兼容” 的特性,广泛用于以下场景:

  • 跨语言 / 跨平台通信:如微服务间的 RPC 调用(如 gRPC 框架默认使用 Protobuf 作为数据格式)、客户端与服务器的通信(如游戏客户端与服务端的数据交互);
  • 数据存储:如日志存储(二进制格式比文本格式节省磁盘空间)、分布式系统中的数据同步;
  • 大数据传输:如实时数据流(如 Flink/Spark 处理的结构化数据)、物联网设备的传感器数据(设备资源有限,需高效传输)。

四、Protobuf 的关键概念补充

  1. 字段编号(Field Number).proto 中每个字段的 = 1/= 2核心标识,而非 “字段顺序”:

    • 编解码时,Protobuf 不依赖字段名,只依赖字段编号(因此字段名修改不影响兼容性,编号不能改);
    • 编号范围:1-536870911(1-15 占 1 字节,16-2047 占 2 字节,常用字段优先用小编号以节省体积)。
    • 19000 ~19999不可⽤, 因为:在Protobuf协议的实现中,对这些数进⾏了预留。如果⾮要在.proto ⽂件中使⽤这些预留标识号,例如将name字段的编号设置为19000,编译时就会报警
  2. proto3 与 proto2 的区别目前主流使用 proto3,相比 proto2 简化了语法:

    • proto3 移除了 required(必填)和 optional(可选)关键字,所有字段默认 “可选”;
    • proto3 支持更多语言(如 Go、Rust),且默认值规则更统一(如 int32 默认 0,string 默认空串);
    • proto3 支持 map 类型(如 map<int32, string> id_to_name = 4),proto2 需手动模拟。
  3. 二进制格式的不可读性Protobuf 序列化后是二进制数据,无法直接用文本编辑器查看(与 JSON/XML 不同),需用专门工具(如 protoc --decode)解析:

    # 从二进制文件 person.bin 解析出数据(需指定 .proto 定义)
    protoc --decode=user.Person ./person.proto < ./person.bin
    

Protobuf (.proto) 文件语法规则

0. 数据类型
(1)标量类型
.proto 类型C++ 对应类型Java 对应类型特点与用途注意事项
doubledoubledouble64 位浮点数精度较高,适合需要大范围数值的场景(如坐标、科学计算)
floatfloatfloat32 位浮点数精度较低但占用空间小,适合存储传感器数据等对精度要求不高的场景
int32int32int32 位整数(变长编码)负数会编码为 10 字节,不适合存储负数(建议用sint32
int64int64long64 位整数(变长编码)负数会编码为 10 字节,不适合存储负数(建议用sint64
uint32uint32int32 位无符号整数(变长编码)仅用于非负整数,编码效率高于int32(无符号时)
uint64uint64long64 位无符号整数(变长编码)仅用于非负整数,编码效率高于int64(无符号时)
sint32int32int32 位有符号整数(变长编码, ZigZag 编码)专门优化负数存储,负数编码后体积比int32小(推荐存储可能为负的整数)
sint64int64long64 位有符号整数(变长编码, ZigZag 编码)专门优化负数存储,负数编码后体积比int64小(推荐存储可能为负的整数)
fixed32uint32int32 位无符号整数(固定 4 字节)数值较大时(>2^28)编码效率高于uint32
fixed64uint64long64 位无符号整数(固定 8 字节)数值较大时(>2^56)编码效率高于uint64
sfixed32int32int32 位有符号整数(固定 4 字节)适合存储绝对值较大的负数(无需变长编码开销)
sfixed64int64long64 位有符号整数(固定 8 字节)适合存储绝对值较大的负数(无需变长编码开销)
boolboolboolean布尔值(true/false)编码后占 1 字节,无默认值(未设置时视为false
stringstd::stringStringUTF-8 编码的文本(长度不超过 2^32)必须是合法的 UTF-8 字符,适合存储短文本(长文本建议用bytes
bytesstd::stringByteString原始字节序列(长度不超过 2^32)适合存储二进制数据(如图片、压缩数据)或非 UTF-8 文本
(2)复合类型
类型定义示例特点与用途注意事项
枚举(enum)protobuf enum Status { OK = 0; ERROR = 1; }有限的离散值集合1. 首元素必须为 0(作为默认值);2. 可通过 option allow_alias = true; 允许枚举值别名;3. 枚举值范围:0~2^32-1
消息(message)protobuf message Person { string name = 1; }自定义结构化数据(可嵌套)1. 可嵌套定义(如 message A { message B {} });2. 可作为其他消息的字段类型;3. 支持递归引用(需用 import "google/protobuf/any.proto";
repeated 字段protobuf repeated int32 ids = 1;动态数组(类似 List/Vector)1. 编码效率:proto3 中采用 “长度前缀” 编码,比 proto2 更高效;2. 空数组与未设置等价,size() 为 0
map 类型protobuf map<int32, string> id_name = 1;键值对集合(类似字典)1. 键类型:除 float/double/bytes 外的标量类型;2. 无序存储,迭代顺序不保证;3. 不可用 repeated 修饰
(3)特殊类型
类型定义示例特点与用途注意事项
Anyprotobuf import "google/protobuf/any.proto"; message Data { google.protobuf.Any value = 1; }动态类型(可存储任意消息)1. 需要导入 any.proto;2. 需通过 PackFrom()/UnpackTo() 序列化 / 反序列化;3. 适合存储不确定类型的数据
Oneofprotobuf message Result { oneof value { int32 num = 1; string str = 2; } }多个字段中最多一个被设置(节省空间)1. 设置新字段会自动清除之前的字段;2. 不能包含 repeated 字段;3. 可通过 which_value() 判断哪个字段被设置
(4)通用注意事项
  1. 字段编号(Field Number)
    • 范围:1~536870911(1~15 占 1 字节,16~2047 占 2 字节,建议常用字段用小编号);
    • 一旦使用,不可修改或重复使用(影响兼容性);
    • 保留字段:用 reserved 1, 2;reserved "name"; 防止未来误用已废弃的编号 / 名称。
  2. 默认值(Default Values)
    • proto3 中所有字段默认 “可选”,未设置时使用默认值:
      • 数值类型:0;布尔值:false;字符串 /bytes:空;枚举:第一个值(0);消息:默认实例(所有字段为默认值)。
    • has_xxx() 函数在 proto3 中对标量类型无效(默认值不视为 “被设置”)。
  3. 兼容性
    • 新增字段:不影响旧版本解析(旧版本会忽略新增字段);
    • 删除字段:建议标记为 reserved 避免复用编号;
    • 类型变更:仅允许兼容类型(如 int32int64 兼容,int32string 不兼容)。
  4. 编码效率
    • 变长类型(如 int32/sint32)适合小数值,固定长度类型(如 fixed32)适合大数值;
    • 负数优先用 sint32/sint64(ZigZag 编码更高效);
    • 避免嵌套过深的消息(影响解析效率)。
  5. 命名规范
    • 消息 / 枚举名:帕斯卡命名法(如 PersonInfo);
    • 字段 / 枚举值:蛇形命名法(如 user_nameerror_code);
    • 包名:小写字母 + 下划线(如 my_project)。
1. 版本声明

必须指定 protobuf 版本(通常使用 proto3):

syntax = "proto3";  // 声明使用 proto3 语法
2. 包声明

类似c++中的namespace命名空间,用来避免命名冲突:

package myproject;  // 包名,生成代码时会作为命名空间
3. 消息定义(Message)

数据结构的核心,类似类或结构体:

message Person {string name = 1;    // 字段名 = 字段编号(1-15 占1字节,建议常用字段用)int32 id = 2;       // 字段类型 + 名称 + 编号bool is_student = 3;repeated string hobbies = 4;  // repeated 表示可重复(类似数组)
}
4. 字段类型
  • 标量类型:int32/int64float/doubleboolstringbytes

  • 枚举(Enum):

    enum Gender {UNKNOWN = 0;  // 枚举默认值必须为 0MALE = 1;FEMALE = 2;
    }
    
  • 嵌套消息:

    message Student {Person info = 1;  // 嵌套使用其他消息类型int32 grade = 2;
    }
    
5. 服务定义(Service)

用于 RPC 通信:

service UserService {rpc GetUserInfo(UserRequest) returns (UserResponse);
}

生成代码的使用方法

1. 编译 .proto 文件

使用 protoc 编译器生成 C++ 代码:

protoc --cpp_out=./ ./person.proto

会生成两个文件:

  • person.pb.h:头文件,包含类定义
  • person.pb.cc:实现文件

2. 核心类与函数

假设生成的类为 Person(对应 .proto 中的 message):

(1)对象创建与字段设置
#include "person.pb.h"
using namespace myproject;  // 对应 package 声明int main() {Person person;// 设置字段(生成的 setter 函数)person.set_name("Alice");       // string 类型person.set_id(1001);            // int32 类型person.set_is_student(true);    // bool 类型// repeated 字段(类似 vector)person.add_hobbies("reading");person.add_hobbies("sports");// 获取 repeated 字段数量int hobby_count = person.hobbies_size();// 获取指定索引的元素std::string first_hobby = person.hobbies(0);
}
(2)序列化与反序列化
  • 序列化:将对象转为二进制数据

    std::string data;
    person.SerializeToString(&data);  // 序列化为字符串
    // 或写入文件
    std::fstream output("person.bin", std::ios::out | std::ios::binary);
    person.SerializeToOstream(&output);
    
  • 反序列化:从二进制数据恢复对象

    Person new_person;
    new_person.ParseFromString(data);  // 从字符串解析
    // 或从文件读取
    std::fstream input("person.bin", std::ios::in | std::ios::binary);
    new_person.ParseFromIstream(&input);
    
(3)字段访问与判断
// 判断字段是否被设置(proto3 中默认值不视为"被设置")
if (person.has_id()) {int id = person.id();  // 获取字段值
}// 清除字段值
person.clear_name();

3. 核心函数参数详解

函数作用参数说明
set_xxx(value)设置字段值value:与字段类型匹配的值
xxx()获取字段值无参数,返回字段类型的值
has_xxx()判断字段是否被显式设置无参数,返回 bool
clear_xxx()清除字段值无参数
add_xxx(value)向 repeated 字段添加元素value:元素值
xxx_size()获取 repeated 字段元素数量无参数,返回 int
xxx(index)获取 repeated 字段指定元素index:元素索引(0 开始)
SerializeToString()序列化为字符串输出参数:std::string* 用于存储结果
ParseFromString()从字符串反序列化输入参数:const std::string& 二进制数据
SerializeToOstream()序列化到输出流输入参数:std::ostream*(如文件流)
ParseFromIstream()从输入流反序列化输入参数:std::istream*(如文件流)

注意事项

  1. 字段编号一旦确定,不应修改(影响兼容性)
  2. proto3 移除了 proto2 中的 requiredoptional 关键字
  3. 生成的代码需链接 protobuf 库(编译时加 -lprotobuf
  4. 二进制格式不兼容不同版本的 .proto 定义,需做好版本管理
http://www.dtcms.com/a/432068.html

相关文章:

  • 网站服务器 电信推广码怎么填
  • 数据驱动+AI赋能:互联网项目管理全流程的技术破局与方法论实践
  • 贵阳网站建设设计手机做任务的网站
  • AI大模型:(三)1.4 Dify聊天助手快速搭建智能淘宝店小二
  • dw做网站怎么排版网站开发应财务如何记账
  • 网站开发流行工具如何知道一个网站做的什么推广
  • 网站建设方面书籍手机网站导航特效
  • 网站策划包括哪些内容中国哪家网站做仿古做的好
  • 常用的建一个网站要多少钱四川省和城乡建设厅网站
  • 笔记:标准化和归一化的学习
  • 培训学校类网站建设方案1软装包括哪些
  • 计算机网络【第三章-数据链路层】
  • 网站 建设 内容 安排济南seo整站优化厂家
  • Ubuntu 中获取指定软件依赖安装包
  • 利用CountDownLatch批量处理解决实际业务问题
  • 网站建设公司新员工培训ppt模板北京发布紧急通知2023年
  • 移动端网站怎么做软件界面设计软件有哪些
  • Linux系统C语言中与文件操作相关的核心库函数讲解
  • 大数据成矿预测系列(二) | 证据权重法如何克服自身局限?
  • 基于springboot的家校合作管理系统
  • 湖南网站建设 搜搜磐石网络网站首页图片素材
  • STM32控制开发学习笔记【基于STM32 HAL库】
  • 滕州做网站比较好的网络公司网页设计十大品牌
  • 【项目】基于Spring全家桶的论坛系统 【上】
  • 北京移动端网站公司基于PHP的家教网站开发环境
  • 网站毕设怎么做网站开发合同技术目标
  • 成都市建设网站首页kol营销
  • MySQL学习笔记07:MySQL SQL优化与EXPLAIN分析实战指南(上):执行计划深度解析
  • 产品经理指南:Vibes与AI提示词驱动短视频创新与Instagram优化
  • 手机上怎样制作网站广州市做网站公司