protobuf认识与使用及环境安装

1. protobuf简介 【protobuf认识与使用及环境安装】Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。目前提供了 C++、Java、Python 三种语言的 API。
Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成代码并实现将结构化数据序列化的功能。这里我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言RPC接口的基础工具。
更多资料可查看:https://developers.google.com/protocol-buffers/

需要了解两点
  1. protobuf是类似与json一样的数据描述语言(数据格式)
  2. protobuf非常适合于RPC数据交换格式
接着我们来看一下protobuf的优势和劣势:
优势:
1:序列化后体积相比Json和XML很小,适合网络传输
2:支持跨平台多语言
3:消息格式升级和兼容性还不错
4:序列化反序列化速度很快,快于Json的处理速度
劣势:
1:应用不够广(相比xml和json)
2:二进制格式导致可读性差
3:缺乏自描述
2. protobuf的安装 2.1 windows平台(vs2017)
  • 安装protobuf
    • 下载protobuf的C++版本的源代码,地址:https://github.com/google/protobuf/releases
      • 下载 protobuf-cpp-3.8.0.zip/tar.gz 这个包
    • 解压源码 (路径不要带中文)
  • 安装cmake,下载地址:https://cmake.org/download/
  • 使用cmake 生成 vs2017 工程
也可使用以下链接下载:
链接:https://pan.baidu.com/s/1NiOIb7hjhGoXj1_fkGQfyQ
提取码:x6xm
protobuf认识与使用及环境安装
文章图片

protobuf认识与使用及环境安装
文章图片

执行完这一步之后,需要稍等一小会儿。
protobuf认识与使用及环境安装
文章图片

  • 进入 vs2017 工程目录,使用vs2017打开,F7 编译
  • 编译完成 ,在 vs2017工程目录 /Debug 目录下,可以看到生成的库文件
  • 将生成的动态库和头文件放到自定义目录中备用
  • 在vs中指定头文件目录和库目录(根据自己上一步实际的存储目录进行指定)
  • 修改vs其他配置(这个很重要 )
    • 修改预处理器定义:
      • 项目属性->c/c++ -> 预处理器 -> 预处理器定义添加这个PROTOBUF_USE_DLLS宏定义
2.2Linux(C)下的安装
参考资料: https://github.com/protocolbuffers/protobuf/tree/master/src
  • 下载源码安装包: protobuf-cpp-3.8.0.tar.gz
  • 解压缩
$ tar zxvf protobuf-cpp-3.8.0.tar.gz

  • 安装 -> 进入到解压目录
$ cd protobuf-3.8.0 $ ./configure $ make $ make check (可选) $ sudo make install

2.3Linux(go)下的安装
1.下载protobuf
方法一:===> git clone https://github.com/protocolbuffers/protobuf.git方法二:===> 或者将准备好的压缩包进行拖入 链接:https://pan.baidu.com/s/1FcidcGTk-_pdmM-5PKjvwg 提取码:pk2f 解压到$GOPATH/src/github.com/protocolbuffers/下面 Unzip protobuf.zip

2.安装(ubuntu)
(1)安装依赖工具(联网) $ sudo apt-get install autoconf automake libtool curl make g++ unzip libffi-dev -y(2)进入protobuf文件 cd protobuf/(3)进行安装检测 并生成自动安装脚本 ./autogen.sh ./configure(4)进行编译C代码 make(5)进行安装 sudo make install(6)刷新linux共享库关系 sudo ldconfig

3.测试protobuf编译工具
protoc -h

如果正常输出 相关指令 没有报任何error,为安装成功
4.安装protobuf的go语言插件
由于protobuf并没直接支持go语言需要我们手动安装相关插件
(1)下载 方法一:===> go get -v -u github.com/golang/protobuf/proto 方法二:===>或者将 github.com-golang-protobuf.zip拖入 进行解压到 $GOPATH/src/github.com/golang(2)进入到文件夹内进行编译 $ cd $GOPATH/src/github.com/golang/protobuf/protoc-gen-go $ go build(3)将生成的 protoc-gen-go可执行文件,放在/bin目录下 $ sudo cp protoc-gen-go /bin/(4)尝试补齐protoc-gen-go 如果可以补齐代表成功,如果执行不报错 代表工具成功

3. protobuf简单语法 参考文档:https://developers.google.com/protocol-buffers/docs/proto3
简单示例:
syntax = "proto3"; //指定版本信息,不指定会报错 package pb; //后期生成go文件的包名 //message为关键字,作用为定义一种消息类型 message Person{ //名字 string name = 1; //年龄 int32age = 2 ; }enum test{ int32 age = 0; }

  • protobuf消息的定义(或者称为描述)通常都写在一个以 .proto 结尾的文件中。
  • 该文件的第一行指定正在使用proto3语法:如果不这样做,协议缓冲区编译器将假定正在使用proto2。这也必须是文件的第一个非空的非注释行。
  • 第二行package指明当前是pb包(生成go文件之后和Go的包名保持一致)
  • 最后message关键字定义一个Person消息体,类似于go语言中的结构体,是包含一系列类型数据的集合。许多标准的简单数据类型都可以作为字段类型,包括boolint32floatdouble,和string。也可以使用其他message类型作为字段类型。
  • 在message中有一个字符串类型的value成员,该成员编码时用1代替名字。我们知道,在json中是通过成员的名字来绑定对应的数据,但是Protobuf编码却是通过成员的唯一编号来绑定对应的数据,因此Protobuf编码后数据的体积会比较小,能够快速传输,缺点是不利于阅读。
    message的格式说明:
    消息由至少一个字段组合而成,类似于Go语言中的结构体,每个字段都有一定的格式:
    数据类型 字段名称 = 唯一的编号标签值;
    唯一的编号标签:代表每个字段的一个唯一的编号标签,在同一个消息里不可以重复。这些编号标签用与在消息二进制格式中标识你的字段,并且消息一旦定义就不能更改。需要说明的是标签在1到15范围的采用一个字节进行编码,所以通常将标签1到15用于频繁发生的消息字段。编号标签大小的范围是1到2的29次。19000-19999是官方预留的值,不能使用。
4. protobuf高级语法 4.1message嵌套
messsage除了能放简单数据类型外,还能存放另外的message类型,如下:
syntax = "proto3"; //指定版本信息,不指定会报错 package pb; //后期生成go文件的包名 //message为关键字,作用为定义一种消息类型 message Person{ //名字 string name = 1; //年龄 int32age = 2 ; //定义一个message message PhoneNumber { string number = 1; int64 type = 2; } PhoneNumber phone = 3; }

4.2 repeated关键字
repeadted关键字类似与go中的切片,编译之后对应的也是go的切片,用法如下:
syntax = "proto3"; //指定版本信息,不指定会报错 package pb; //后期生成go文件的包名 //message为关键字,作用为定义一种消息类型 message Person{ //名字 string name = 1; //年龄 int32age = 2 ; //定义一个message message PhoneNumber { string number = 1; int64 type = 2; } repeated PhoneNumber phone = 3; }

4.3 默认值
解析数据时,如果编码的消息不包含特定的单数元素,则解析对象对象中的相应字段将设置为该字段的默认值。不同类型的默认值不同,具体如下:
对于字符串,默认值为空字符串。 对于字节,默认值为空字节。 对于bools,默认值为false。 对于数字类型,默认值为零。 对于枚举,默认值是第一个定义的枚举值,该值必须为0。 repeated字段默认值是空列表 message字段的默认值为空对象

4.4 enum关键字
在定义消息类型时,可能会希望其中一个字段有一个预定义的值列表。比如说,电话号码字段有个类型,这个类型可以是,home,work,mobile。我们可以通过enum在消息定义中添加每个可能值的常量来非常简单的执行此操作。
enum也可以为不同的枚举常量指定相同的值来定义别名。如果想要使用这个功能必须讲allow_alias选项设置为true,负责编译器将报错。示例如下:
syntax = "proto3"; //指定版本信息,不指定会报错 package pb; //后期生成go文件的包名 //message为关键字,作用为定义一种消息类型 message Person{ //名字 string name = 1; //年龄 int32age = 2 ; //定义一个message message PhoneNumber { string number = 1; PhoneType type = 2; }repeated PhoneNumber phone = 3; }//enum为关键字,作用为定义一种枚举类型 enum PhoneType { //如果不设置将报错 option allow_alias = true; MOBILE = 0; HOME = 1; WORK = 2; Personal = 2; }

如上,enum的第一个常量映射为0,每个枚举定义必须包含一个映射到零的常量作为其第一个元素。这是因为:
  • 必须有一个零值,以便我们可以使用0作为数字默认值。
  • 零值必须是第一个元素,以便与proto2语义兼容,其中第一个枚举值始终是默认值。
4.5 oneof关键字
如果有一个包含许多字段的消息,并且最多只能同时设置其中的一个字段,则可以使用oneof功能,示例如下:
message Person{ //名字 string name = 1; //年龄 int32age = 2 ; //定义一个message message PhoneNumber { string number = 1; PhoneType type = 2; }repeated PhoneNumber phone = 3; oneof data{ string school = 5; int32 score = 6; } }

4.6 在一个proto文件中导入其他proto文件
  • 一个proto文件
syntax = "proto3"; // 第一行// 导入 Info.proto 文件到当前文件中 import "Info.proto"; enum Color { Red = 0; Green = 6; Blue = 9; Pink = 7; }message Person { int32 id = 1; repeated bytes name = 2; // 代表数组,可以存多个数据 string sex = 3; int32 age = 4; Color color = 5; Info info = 6; }

  • 另一个proto文件
syntax = "proto3"; message Info { bytes address = 1; //家庭住址 int32 num = 2; // 门牌号 }

  • 将 Person.proto 和 Info.proto 各自调用 protoc 命令,各自生成对应的 xxxpb.h 和 xxxpb.cc
  • 将 xxxpb.h 和 xxxpb.cc 导入VS项目中。
  • 初始化Person时,初始化 Info
Info *info = p.mutable_info(); info->set_address("地址值"); info->set_num("门牌号");

  • 解析获取Person 是,获取 Info数据。
Info ii = p.info(); ii.address(); // 获取地址值 ii.num(); // 获取门牌号

4.7 定义RPC服务
如果需要将message与RPC一起使用,则可以在.proto文件中定义RPC服务接口,protobuf编译器将根据你选择的语言生成RPC接口代码。示例如下:
//定义RPC服务 service HelloService { rpc Hello (Person)returns (Person); }

5. protobuf基本编译 5.1 Linux下的编译
protobuf编译是通过编译器protoc进行的,通过这个编译器,我们可以把.proto文件生成go,Java,Python,C++, Ruby, JavaNano, Objective-C,或者C# 代码,生成命令如下:
protoc --proto_path=IMPORT_PATH --go_out=DST_DIRpath/to/file.proto

  1. –proto_path=IMPORT_PATH,IMPORT_PATH是 .proto 文件所在的路径,如果忽略则默认当前目录。如果有多个目录则可以多次调用–proto_path,它们将会顺序的被访问并执行导入。
  2. –go_out=DST_DIR, 指定了生成的go语言代码文件放入的文件夹
    –cpp_out= 即可用于C++语言中
  3. 允许使用 protoc --go_out=./ *.proto 的方式一次性编译多个 .proto 文件
  4. go语言编译时,protobuf 编译器会把 .proto 文件编译成 .pd.go 文件
一般在使用的时候我们都是使用下面这种简单的命令:
protoc --go_out=./ *.proto

当我们给这个.proto文件中添加一个RPC服务,进行编译时略有不同需指定编译插件。在protoc-gen-go内部已经集成了一个叫grpc的插件,可以针对grpc生成代码:
gRPC的安装及使用可看此博客: https://blog.csdn.net/bigfaceCatzzZ/article/details/104526095}
protoc --go_out=plugins=grpc:. *.proto

5.2 windows下的编译
5.2.1 操作流程 1.准备待序列化的数据。
2.创建 “xxxx.proto” 文件,指定使用protobuf版本(2、3)
3.按 protobuf 语法,将数据组织到文件中。
4.使用 命令 protoc 生成 xxxpb.h 和 xxxpb.cc
- 将 bin/ 配置到环境变量 PATH 中。C:\protobuf\bin
- 将 lib/ 配置到环境变量 PATH 中。C:\protobuf\lib
5.将 .h 和 .cc 添加到 VS 项目中。实现 序列化、反序列化。
  • 读API
    • xxx();
  • 写API
    • set_xxx();
  • 序列化 : SerializePartialToString()
  • 反序列化:ParseFromString()
5.2.2 测试使用protobuf编解码
  • 准备工作:
  1. 将 xxx.pb.cc 和 xxx.pb.h 添加到项目中。
  2. 项目右键 – 属性 – VC++目录 – 包含目录 – 写入 C:\protobuf\include
  3. 项目右键 – 属性 – VC++目录 – 库目录 – 写入 C:\protobuf\lib
  4. 项目右键 – 属性 – 链接器 – 输入 – 附加依赖项 – 写入 libprotobufd.lib
  5. 项目右键 – 属性 – C/C++ – 预处理器 – 预处理器定义 – 写入 PROTOBUF_USE_DLLS 宏。
  • 测试使用流程:
    1.创建Person类对象,并初始化。
    2.将 person类进行序列化 SerializePartialToString(),得到 string。
    3.网络传输。
    4.将得到的string,进行反序列化 ParseFromString(),得到Person 对象。
    5.打印查看 类成员。

    推荐阅读