Thrift 的序列化机制

12 篇文章 1 订阅

Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 

1、构造应用场景:

1)首先我们先来定义下thrift的简单结构.

namespace java mmxf.thrift;

struct Pair {
  1: required string key
  2: required string value
}

required修饰符你肯定能猜测到它的意义, 但是你是否 有没有这样的疑惑, "1", "2" 这些数字标识符究竟有何含义? 它在序列化机制中究竟扮演什么样的角色?

2)编译并进行

thrift -gen java <your thrift file>

3)编写测试代码

private String datafile = "1.dat";
  
// *) 把对象写入文件
public void writeData() throws IOException, TException {
  Pair pair = new Pair();
  pair.setKey("rowkey").setValue("column-family");

  FileOutputStream fos = new FileOutputStream(new File(datafile));
  pair.write(new TBinaryProtocol(new TIOStreamTransport(fos)));
  fos.close();
} 

调用writeData(), 把 pair{key=> rowkey, value=> column-family}  写入文件1.dat中

4)如果我重新定义pair结构, 调整数字编号数序

struct Pair {
  2: required string key
  1: required string value
}

评注: 这边2对应key, 1对应value. 重新编译thrift -gen java <your thrift file>


5)然后读取该数据

private String datafile = "1.dat";
// *) 从文件恢复对象
public void readData() throws TException, IOException {
  FileInputStream fis = new FileInputStream(new File(datafile));

  Pair pair = new Pair();
  pair.read(new TBinaryProtocol(new TIOStreamTransport(fis)));

  System.out.println("key => " + pair.getKey());
  System.out.println("value => " + pair.getValue());

  fis.close();
}
调用readData(), 从文件1.dat中恢复Pair对象来 结果:

key => column-family
value => rowkey


是不是和你预期的相反, 看来属性名称并没有发挥作用, 而id标识在thrift的序列化/反序列化扮演非常重要的角色。带着这些疑惑, 我们进一步的详细解读序列化机制。


thrift 数据格式描述:(官网文档描述: http://thrift.apache.org/static/files/thrift-20070401.pdf)
thrift的向后兼容性(Version)借助属性标识(数字编号id + 属性类型type)来实现, 可以理解为在序列化后(属性数据存储由 field_name:field_value => id+type:field_value) , 这也解释了上述提到的场景的原因了.

对之前定义的Pair结构体, 进行代码解读:

public void read(org.apache.thrift.protocol.TProtocol iprot, Pair struct) { 
  // *) 读取结构结束标记
  iprot.readStructBegin();
  while ( iprot is stop) {
    // *) 读取Field属性开始标记
    schemeField = iprot.readFieldBegin();
    // *) field标记包含 id + type, switch根据(id+type)来分配相关的值
    switch (schemeField.id) {
      case <id>: // <field_name>
        if (schemeField.type == thrift.TType.<type>) {
          struct.<field_name> = iprot.read<type>();
          struct.set<field_name>IsSet(true);
        }
    }
    // *) 读取Field属性结束标记
    iprot.readFieldEnd();
  }
  // *) 读取结构体结束标记
  iprot.readStructEnd();
}

代码评注:
从恢复对象的函数中, 我们也可以对thrift定义的序列化对象有个初步的认识, 庖丁解牛,最终会被细化为readStructBegin, readFieldBegin, read<type>(readString, readI32, readI64)的有组织有序调用.


2、数据交换格式分类
当前的数据交换格式可以分为如下几类:
1.)自解析型
序列化的数据包含完整的结构, 包含了field名称和value值. 比如xml/json/java serizable, 大百度的mcpack/compack, 都属于此类. 即调整不同属性的顺序对序列化/反序列化不影响.


2)半解析型
序列化的数据,丢弃了部分信息, 比如field名称, 但引入了index(常常是id+type的方式)来对应具体属性和值. 这方面的代表有google protobuf, thrift也属于此类.


3)无解析型
传说中大百度的infpack实现, 就是借助该种方式来实现, 丢弃了很多有效信息, 性能/压缩比最好, 不过向后兼容需要开发做一定的工作, 详情不知.

thrift与常见数据交换格式的对比

交换格式 类型 优点 缺点
Xml 文本 易读 臃肿, 不支持二进制数据类型
Json 文本 易读 丢弃了类型信息, 比如"score":100, 对score类型是int/double解析有二义性, 不支持二进制数据类型
Java serizable 二进制 使用简单 臃肿, 只限制在java领域
Thrift 二进制 高效 不宜读, 向后兼容有一定的约定限制
Google Protobuf 二进制 高效 不宜读, 向后兼容有一定的约定限制

向后兼容实践:
Thrift官方文档, 也提到对新增的字段属性, 采用id递增的方式标识并以optional修饰来添加.






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值