Thrift与Protocol Buffers
在大量调用或者需要传输大量数据的时候,数据编码之后的体积大小就是一个尤为关键的因素。Apache Thrift和Protocol Buffers(protobuf)就是业界最为知名的两种二进制编码格式。其中前者由Facebook发起并贡献给了Apache作为开源项目,后者则有Google发起并开源。两者格式有很多相似之处,一般都是作为接口定义语言(IDL)配合RPC传输协议使用。
# 一个thrift例子
struct Person {
1: required string userName,
2: optional i64 favoriteNumber,
3: optional list<string> interests
}
# 一个protobuf例子
message Person {
required string user_name = 1;
optional int64 favorite_number = 2;
repeated string interests = 3;
}
相比之前提到的JSON格式,这两者最大的优势在于编码时避免使用完整键的值,而是使用域标签作为键(通常是1,2,3这样的整形数字)。在Thrift和protobuf里,域标签就代表一个字段,而不用完整的键来代表一个字段。
需要提到的一点是,在字段标示属性由一个optional
或者required
字段。这个属性表明该字段是否是必填,required的校验是在运行时做的,编码时这两种属性并没有实质差异。
来看一下,thrift和protobuf是怎么处理向前兼容和向后兼容的。就像上面说的,字段是和域标签一对一绑定的。也就是说字段名可以变换,而对于同一字段的域标签则要保持不变。当需要添加新字段时,将新的域标签绑定到新字段上即可。对于旧代码读取新数据的情况,未被代码端标示的字段将被自动抛弃而不影响其他字段的读取,由此可见,向前兼容可以被轻松实现。
由于域标签在struct层级保持唯一,新代码可以轻松按照域标签读取旧数据。需要注意的一点是,新添加的字段不能被标记成required,否则在新代码读取旧数据时发生字段缺少的问题。同样的,删除字段时也应该只删除optional字段。域标签和字段的绑定关系应该保持不变,不能用新的字段绑定旧的域标签。
更换字段的数据类型不会发生报错,但是可以会出现精度损失或者溢出的情况(旧代码端的i32读取新数据模式的i64可能会出现溢出情况,旧代码的i32读取新数据模式的float则会出现精度损失)。
Avro
Apache Avro是另一种二进制数据格式。它是Hadoop项目的一个子项,致力于解决HDFS内的数据传输。
Avro允许读写两种数据模式的存在。读写两种模式之间的字段按照字段名进行匹配。
相比JSON和XML,二进制数据格式有下面这些优点:
- 编码之后拥有更小的数据体积(因为采用域标签来代替完整的域的值)
- 数据声明本身就可以当做是接口文档使用,而且保证是线上运行的最新版本。
- 对于静态语言而言,在编译时做类型检查的特性可以规避很多问题。
网友评论