Protobuf的定义
protobuf是一种用于序列化结构数据的工具,实现数据的存储与交换,与平台和语言无关。
序列化: 将结构数据或对象转换成能够用于存储和传输的格式。
反序列化: 在其它计算机环境中,将序列化后的数据还原为结构数据或对象
定义的数据结构,然后使用protoc编译生成源代码,在各种数据流中使用各种语言进行编写和读取结构数据。甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
Protobuf的优点
- 优点:
- 性能高效:与xml相比,protobuf更小(3-10倍),更快(20-100倍),更为简单。
- 语言无关、平台无关,protobuf支持c++、golang、java、python等多种语言,多个平台。
- 扩展性、兼容性强,只需要使用protobuf对结构数据进行一次描述,即可从各种数据流中读取结构数据,更新数据结构时不会破坏原有程序。
- 缺点:
- 不适合用来对基于文本的标记文档(如 HTML)建模。
- 自解释性较差,数据存储格式为二进制,需要通过proto文件才能了解到其内部的数据结构。
Protobuf的使用流程
- protobuf在linux下的安装过程
# 安装所需依赖
apt install autoconf automake libtool curl make g++ unzip
wget https://github.com/protocolbuffers/protobuf/releases/download/v21.6/protobuf-all-21.6.tar.gz
tar -xzvf protobuf-all-21.6.tar.gz
cd protobuf-21.6/
./configure
make #时间有点长
make check #时间有点长
make install
ldconfig # refresh shared library cache.
这时一般来说
libprotobuf库在/usr/local/lib路径下;protoc一般在/usr/local/bin路径下 。如下:
# ls /usr/local/lib/
libprotobuf.a libprotobuf-lite.so.32 libprotoc.a pkgconfig
libprotobuf.la libprotobuf-lite.so.32.0.6 libprotoc.la python2.7
libprotobuf-lite.a libprotobuf.so libprotoc.so python3.7
libprotobuf-lite.la libprotobuf.so.32 libprotoc.so.32
libprotobuf-lite.so libprotobuf.so.32.0.6 libprotoc.so.32.0.6
# ls /usr/local/bin/
chardetect docker-compose f2py3.7 ipython jsonpointer pip3.7 pygmentize
cloud-id easy_install fonttools ipython3 jsonschema protoc ttx
cloud-init f2py iptest jsondiff pip pyftmerge wrk
cloud-init-per f2py3 iptest3 jsonpatch pip3 pyftsubset
检测protobuf是否安装成功:protoc --version
安装protoc-gen-go
- 下载安装
git clone https://github.com/golang/protobuf.git
cd protobuf/protoc-gen-go/
go build -o protoc-gen-go main.go
# 即可在当前目录,看到已经生成的 protoc-gen-go 可执行文件。
# 将生成的protoc-gen-go文件复制到环境变量当中去
cp protoc-gen-go /usr/local/bin/
使用举例
- persion.proto文件
syntax = "proto3";
option go_package = "./";
message Person {
string name = 1;
uint32 age = 2;
uint64 phnum = 3;
}
- 编译persion.proto文件,执行如下指令对.proto文件按照c++、python、go形式编译,此时会生成相对应的pb.cc/pb.h、*py2.py、.pb.go文件。
#推荐这种顺序
protoc person.proto --cpp_out=./ #末尾的'./'表示生成的cc、h文件存放在当前目录(可调)
protoc person.proto --python_out=./ #使用python进行编译
protoc person.proto --go_out=./ #使用go进行编译。注意原生protoc不包含go版本的插件,需额外装protoc-gen-go(其实就是把可执行文件protoc-gen-go放到默认搜索路径即可如/bin)
#当然调换一下参数顺序也是一样的
protoc --cpp_out=./ person.proto
protoc --python_out=./ person.proto
protoc --go_out=./ person.proto
#另外也是可以一次性编很多文件的,如下:
protoc com.tencent.*.proto --python_out=.
protoc com.tencent.epc.qidian.cc*.proto --python_out=.
protoc *.protoc --go_out=.
注意事项1:“=”号后面不能有空格,一个都不能有。否则出错
注意事项2:原生的protoc并不包含Go版本的插件,我们需要额外安装下protoc-gen-go(其实就是把可执行文件protoc-gen-go放到可搜索路径例如/bin下即可)。这个就是protobuf编译插件系列的go版本。
protobuf的应用场景
- 压缩效率高:服务间的海量数据传输与通信,可以节省磁盘和带宽,protobuf适合处理大数据集中的单个小消息,但并不适合处理单个的大消息。
- 解析速度快:可以提高服务器的吞吐能力。
protobuf与json和xml对比
- xml、json、protobuf都具有数据结构化和数据序列化的能力
- xml、json更注重数据结构化,关注可读性和语义表达能力;protobuf更注重数据序列化,关注效率、空间、速度、可读性较差,语义表达能力不足。
- protobuf的应用场景更为明确,xml、json的应用场景更为丰富。
proto3
- 有了proto2, 为什么要引入proto3?
引入proto3的主要意图是在将这个语言推向 google 新的API平台之前清理 protobuf。在 proto3 中,语言被简化,即便于使用又可以用于更大范围的编程语言。同时添加了一些新的特性来更好的支持 API 中的通用习语。 - proto3中的新特性
移除用于原生值字段的字段表述逻辑,移除必填(required)字段,并移除默认值。这显著的简化了 proto3 在实现开放结构标示(open struct representations)中实现,例如在如Android Java, Objective C, 或者 Go 语言中。
移除未知字段
移除扩展(extensions),替代为新的称为 Any 的标准类型
修复未知枚举值的语义
此外还有 map (向后移植到proto2)
此外还有少量用于表述时间,动态日志等的标准类型 (向后移植到proto2)
定义良好的JSON编码,作为二进制编码之外的备选
引入了一个新的概念 “syntax” 来指定一个.proto文件使用的是 proto2 还是 proto3:
如果没有设置,protocol buffer编译器将生成警告而”proto2”会被默认使用。在未来版本中这个警告将可能变成错误。
我们推荐新的Protocol Buffers 用户使用proto3.当然,我们不普遍推荐现有用户从proto2迁移到proto3,因为API不兼容,而我们将继续长期支持proto2.
proto3中的其他重要改动:
在proto3语法中显式的 “optional” 关键字被禁止,因为字段默认就是可选的;必填字段不再被支持
删除非零默认值,而用于非message字段的字段表述逻辑如 has_xxx() 方法被删除;设置为默认值(对于数字字段是0,字符串/字节字段是空)的原生字段在序列化时被跳过
在proto3语法中group字段不再支持
在proto3中默认修改重复原生字段来支持打包序列化(当前版本中在C++, Java, Python中实现)。用户依然可以通过设置packed为false来禁用打包序列化
添加众所周知的类型 protos (any.proto, empty.proto, timestamp.proto, duration.proto 等).用户可以导入并使用这些protos,就像正规的proto文件一样。额外的运行时支持在每个语言中都可用。
Proto3 JSON在一些语言中 (在C++, Java, Python 和 C# 中被完全支持, 在 Ruby 中被部分支持)被支持。 JSON 规范在proto3语言指南中被定义:
普遍
- 在 proto2 和 proto3 语法中同时添加 “reserved” (保留) 关键字。用户可以用这个关键字来声明保留字段数字和名称来防止他们被在同一个消息中的其他字段重用。
为了保留字段数字,在消息中添加保留声明:
syntax = "proto3";
option go_package = "./";
message Student {
string Name = 1;
int32 Age = 4;
reserved 3, 6 to 7, 8;
}
这将保留字段数字 2, 3, 9, 10, 11 and 15。如果用户使用他们中的任何一个作为字段数字,protocol buffer 编译器将报告错误。
字段名字也可以被保留:
message Student {
string Name = 1;
int32 Age = 4;
reserved "Gender";
}
- 添加新的字段选项”json_name”。默认在proto3的JSON格式中 proto 字段名将被转换为”lowerCamelCase”(小写开头的驮峰法)。这个选项被用于覆盖这个行为并为这个字段指定不同的JSON名字。
概述
protocol buffer是一个语言无关、平台无关,可扩展的结构化数据序列化方案,用于协议通讯、数据存储和其它更多用途。
- protocol buffer是什么?
Protocol buffer 是一个灵活,高效,自动化的结构化数据序列化机制 - 想象xml, 但是更小, 更快并且更简单.一旦定义好数据如何构造, 就可以使用特殊的生成的源代码来轻易的读写你的结构化数据到和从不同的数据流,用不同的语言.你甚至可以更新你的数据结构而不打破已部署的使用”旧有”格式编译的程序. - 它们是如何工作的?
通过在.proto文件中定义protocol buffer消息类型来指定要序列化的信息如何组织。每个protocol buffer信息是一个小的信息逻辑记录,包含一序列的”名字-值”对。这里是一个非常基本的例子,.proto文件定义了一个消息,包含一个人的消息:
syntax = "proto3";
option go_package = "./";
message Person {
string name = 1;
int32 age = 2;
string email = 3;
enum PhoneType {
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message PhoneNumber {
string number = 1;
PhoneType type = 2;
}
repeated PhoneNumber phone = 4;
}
一旦定义了消息,可以在.proto文件上运行对应应用语言的protcol buffer的编译器来生成数据访问类。这些类为每个字段(类似name()或者set_name())提供简单的访问器,还有用于序列化/解析整个结构到/从原始字节的方法 - 因此,例如, 如果你选择的语言是c++,在上面的例子上运行编译器将会生成名为Person的类。然后可以用这个类在应用中获取,序列化,并获取 Person protocol buffer消息。可能随后编写一些类似这样的代码:
func main() {
person := hello.Person{
Name: "王五",
Age: 18,
Email: "1341935532@qq.com",
Phone: []*hello.Person_PhoneNumber{
{Number: "15313067271", Type: hello.Person_MOBILE},
},
}
}
为什么不使用xml?
相比xml,protocol buffer在序列化结构数据方面有很多优势:
- 更简单
- 小3-10倍
- 快20-100倍
- 更清晰
- 生成数据访问类,更容易编程使用
例如,假设想要用 name 和 email 来构建一个 Person。在XML中,需要这样做:
<person>
<name>John Doe</name>
<email>jdoe@example.com</email>
</person>
而对应的protocol buffer消息(使用protocol buffer 文本格式):
# protocol buffer的文本展示
# 这 *不是* 实际使用的二进制格式。
person {
name: "John Doe"
email: "jdoe@example.com"
}
当这个消息被编码为protocol buffer 二进制格式(上面的文本格式仅仅是在调试和编辑时方便人阅读的表示方式),它将可能是长28个字节并花费100-200纳秒来解析。XML版本至少需要69个字节,如果删除空白字符,并将话费5000 - 10000 纳秒来解析。
当然,Protocol buffer 也不总是比XML更合适 - 例如,Protocol buffer 不适合建模基于文本的标志(如HTML)文档,因为无法轻易的使用文本交替结构。此外,XML是human-readable 和 human-editable的。Protocol buffer,至少他们原生的格式不是。XML也是某种程度上的自描述。Protocol buffer只有当有消息定义(.proto文件)时才有意义。
我们推荐所有新的gRPC服务器和客户端使用proto3,因为它避免了兼容问题。
protocol buffer被设计用来解决很多这样的问题:
- 可以轻易引入新的字段,而不需要检查这些数据的中介服务器可以简单的解析并传递数据,而无需知道所有字段
- 格式更加自我描述,并可以处理多个语言 (C++, Java, 等)
然而,用户依然需要硬编码他们自己的解析代码。
随着系统的发展,要求一些其他特性和用法:
- 自动生成序列化和反序列化的代码,避免硬解析的要求
- 除了用于短期RPC(remote procedure call)请求外,人们开始使用protocol buffer来作为一个方便的自我描述的格式来存储持久化数据(例如在BigTable中)
- 服务器RPC接口开始定义为protocol文件的一部分,使用protocol编译器生成用户可以用服务器的实际接口来覆盖的桩类。