ProtoBuf 作为一种跨平台、语言无关、可扩展的序列化结构数据的方法,已广泛应用于网络数据交换及存储。随着互联网的发展,系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代Restful之势,而 ProtoBuf 作为g RPC 跨语言、高性能的法宝,我们技术人有必要
深入理解 ProtoBuf 原理,为以后的技术更新和选型打下基础。
我将过去的学习过程以及实践经验,总结成系列文章,与大家一起探讨学习,希望大家能有所收获,当然其中有不正确的地方也欢迎大家批评指正。
本系列文章主要包含:
- 深入理解 ProtoBuf 原理与工程实践(概述)
- 深入理解 ProtoBuf 原理与工程实践(编码)
- 深入理解 ProtoBuf 原理与工程实践(序列化)
- 深入理解 ProtoBuf 原理与工程实践(工程实践)
一、什么是ProtoBuf
ProtoBuf(Protocol Buffers)是一种跨平台、语言无关、可扩展的序列化结构数据的方法,可用于网络数据交换及存储。
在序列化结构化数据的机制中,ProtoBuf是灵活、高效、自动化的,相对常见的XML、JSON,描述同样的信息,ProtoBuf序列化后数据量更小、序列化/反序列化速度更快、更简单。
一旦定义了要处理的数据的数据结构之后,就可以利用ProtoBuf的代码生成工具生成相关的代码。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或从各种不同流中对你的结构化数据轻松读写。
二、为什么是 ProtoBuf
大家可能会觉得 Google 发明 ProtoBuf 是为了解决序列化速度的,其实真实的原因并不是这样的。
ProtoBuf最先开始是 Google用来解决索引服务器 request/response 协议的。没有ProtoBuf之前,Google 已经存在了一种 request/response 格式,用于手动处理 request/response 的编解码。它也能支持多版本协议,不过代码不够优雅:
1 2 3 4 5 | if (protocolVersion=1) { doSomething(); } else if (protocolVersion=2) { doOtherThing(); } ... |
如果是非常明确的格式化协议,会使新协议变得非常复杂。因为开发人员必须确保请求发起者与处理请求的实际服务器之间的所有服务器都能理解新协议,然后才能切换开关以开始使用新协议。
这也就是每个服务器开发人员都遇到过的低版本兼容、新旧协议兼容相关的问题。
为了解决这些问题,于是ProtoBuf就诞生了。
ProtoBuf 最初被寄予以下 2 个特点:
- 更容易引入新的字段,并且不需要检查数据的中间服务器可以简单地解析并传递数据,而无需了解所有字段。
- 数据格式更加具有自我描述性,可以用各种语言来处理(C++, Java 等各种语言)。
这个版本的 ProtoBuf 仍需要自己手写解析的代码。
不过随着系统慢慢发展,演进,ProtoBuf具有了更多的特性:
- 自动生成的序列化和反序列化代码避免了手动解析的需要。(官方提供自动生成代码工具,各个语言平台的基本都有)。
- 除了用于数据交换之外,ProtoBuf被用作持久化数据的便捷自描述格式。
ProtoBuf 现在是 Google 用于数据交换和存储的通用语言。谷歌代码树中定义了 48162 种不同的消息类型,包括 12183 个 .proto 文件。它们既用于 RPC 系统,也用于在各种存储系统中持久存储数据。
ProtoBuf 诞生之初是为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴,“协议缓冲区”。只不过后期慢慢发展成用于传输数据。
Protocol Buffers 命名由来:
三、如何使用 ProtoBuf
3.1 ProtoBuf 协议的工作流程
可以看到,对于序列化协议来说,使用方只需要关注业务对象本身,即 idl 定义,序列化和反序列化的代码只需要通过工具生成即可。
3.2 ProtoBuf 消息定义
ProtoBuf 的消息是在idl文件(.proto)中描述的。下面是本次样例中使用到的消息描述符customer.proto:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | syntax = "proto3"; package domain; |