跨平台的结构化数据传输协议一般情况下关注三要素和三件事:
- 三要素
- protocal
- 数据
- API
- 三件事
- 协议编译
- 序列化
- 反序列化
以下三种协议按照以上要素进行介绍
json(javascript object notation)
- 三要素
- protocal:面向javascript,使用eval()自动转换成远程的对象,面向其他语言需要自己编写model并解析
- 数据:包含多种数据类型,对象(字典表示法),数组(同质化元素集合),string(基本类型,可以用来作为键或者值),nil(空)等
- API:引入json的包,可以提供建立json对象,读入(从网络或者本地)json数据,写出(网络或者本地)json数据的接口,并且提供显式类型转换的接口
- 三件事
- 协议编译:不经过特殊编译,除非针对特定语言产生了特定的转换接口,传输的是文本数据,所以是自描述型的,发送方和接收方事先约定好传输数据的格式
2 序列化:发送方将对象数据手动写入json对象中,由json库自动完成序列化工作
3 反序列化:接受方读出json数据,然后使用json接口获取相应数据写入对象
- 特性
- 因为拥有自描述性,并且以文本方式传输数据,所以可扩展,可编辑
- 拥有良好的跨平台和跨语言特性(提供不同的库)
- 使用范围较广,支持的应用场景较为丰富,灵活的语法可以传输更广泛的数据
- 文本传输,没有经过压缩,并且带有一定的格式扩展,效率比XML优,比protobuf差
注意:不同的json库对数据的处理方式不一样,cjson采用深度拷贝的方式,所以记得销毁数据,而在某些优化场景下是采用浅层拷贝的方式,此时记得考虑数据变动的协同性。
protobuf的概念
- 三要素:
- 协议:编写proto文件,类似于java和c++的类定义,但是更具有通用性(提供各种面向语言的编译库),并且采用编号预处理
- 数据:提供更加丰富的数据类型,基本可以和高级语言一一对应
- API: 根据平台提供各种数据处理的api,同时根据proto文件产生特定的api
- 三件事
- 协议编译:编写proto文件,编译器自动编译,产生特定语言的类型
- 序列化:一切建立在proto生成的类型之上,建立对象,采用数组形式或者api方式写入数据,由库直接序列化成二进制文件,所以相关数据不可编辑;
- 反序列化:读入对象,直接使用(良好的类型匹配)
protobuf的特性
protocol buffers 是一种语言无关(协议的跨语言编译)、平台无关(跨平台编译)、可扩展(可以在proto里面改变类型信息,同时采用动态编译的方式支持无痕扩展)的序列化结构数据的方法,它可用于(数据)通信协议、数据存储等。
Protocol Buffers 是一种灵活,高效,自动化机制的结构数据序列化方法-可类比 XML,但是比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更为简单。
你可以定义数据的结构,然后使用特殊生成的源代码轻松的在各种数据流中使用各种语言进行编写和读取结构数据。你甚至可以更新数据结构,而不破坏由旧数据结构编译的已部署程序。
protobuf的语法
- 将字符串键值编成整形号,提高传输效率,1-15占用一个字节,大于15占用2个字节
- proto文件格式:类似于python
语法规则:syntax "proto3";
在文件开头定义一个:package 文件名;
引入其他文件:import 文件名;
可选字段:option 参数=xxx
使用枚举定义常量:enum {
XXX=xxx;
}
结构体字段(定义一个类型,并给数据成员做编号):message name{
}
最后proto文件会被编译成相应语言的二进制文件
- pb文件编写
数据类型的对应
序列化和反序列化本身不关注数据的传输,但是网络传输的时候,只有拿到整个序列化后的数据,才能解析出对象,所以需要设计协议包括:header(根据不同的对象封装相应的规则,proto),主体
protobuf的不足
- 语义简单,不能提供复杂数据格式,比如XML能够定义的标准形式,XML解析器更加复杂
- 不能对基于文本的标记文档进行建模,一般用于数据结构的建模上面(而文本不适合描述数据结构)
- 数据结构二进制化,没法直接编辑
高级技术
动态编译
- 在一些无法获取已知proto语义的网络中间件中,需要动态的去编译proto文件,这大大提高了protobuf做为结构化数据网络传输的通用性
关于性能的一些细节
-
protobuf的数据编码方式
数据单元编码协议:对于每一个字节,它的第一位用来标志下一个字节是不是属于当前数据. 所以单字节可以用来表示128以下的数字,以此类推。
符号码(key-value):每一个符号码被编码成key-value这两个数据单元
key用来标定value的范围:key=(field_number<<3)|wire,其中field_number为proto中对每一个数据域的编号,wire指以下六种类型
对于负数采用zigzag编码:
封解包的速度:
首先我们来了解一下 XML 的封解包过程。XML 需要从文件中读取出字符串,再转换为 XML 文档对象结构模型。之后,再从 XML 文档对象结构模型中读取指定节点的字符串,最后再将这个字符串转换成指定类型的变量。这个过程非常复杂,其中将 XML 文件转换为文档对象结构模型的过程通常需要完成词法文法分析等大量消耗 CPU 的复杂计算。
反观 Protobuf,它只需要简单地将一个二进制序列,按照指定的格式读取到 C++ 对应的结构类型中就可以了。从上一节的描述可以看到消息的 decoding 过程也可以通过几个位移操作组成的表达式计算即可完成。速度非常快。
references
google官方文档