Apache Thrift系列详解(三) - 序列化机制

前言

Thrift支持二进制压缩格式,以及json格式数据的序列化反序列化。开发人员可以更加灵活的选择协议的具体形式。协议是可自由扩展的,新版本的协议,完全兼容老的版本!

正文

数据交换格式简介

当前流行的数据交换格式可以分为如下几类:

(一) 自解析型

序列化的数据包含完整的结构, 包含了field名称value。比如xml/json/java serizable,大百度的mcpack/compack,都属于此类。即调整不同属性的顺序序列化/反序列化不造成影响。

(二) 半解析型

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

(三) 无解析型

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

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

Thrift的数据类型

  1. 基本类型:
      bool: 布尔值
      byte: 8位有符号整数
      i16: 16位有符号整数
      i32: 32位有符号整数
      i64: 64位有符号整数
      double: 64位浮点数
      string: UTF-8编码的字符串
      binary: 二进制串
  2. 结构体类型:
      struct: 定义的结构体对象
  3. 容器类型:
      list: 有序元素列表
      set: 无序无重复元素集合
      map: 有序的key/value集合
  4. 异常类型:
      exception: 异常类型
  5. 服务类型:
      service: 具体对应服务的类

Thrift的序列化协议

Thrift可以让用户选择客户端服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:

  • TBinaryProtocol:二进制编码格式进行数据传输
  • TCompactProtocol:高效率的、密集二进制编码格式进行数据传输
  • TJSONProtocol: 使用JSON文本的数据编码协议进行数据传输
  • TSimpleJSONProtocol:只提供JSON只写的协议,适用于通过脚本语言解析

Thrift的序列化测试

(a). 首先编写一个简单的thrift文件pair.thrift

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

这里标识了required的字段,要求在使用时必须正确赋值,否则运行时会抛出TProtocolException异常。缺省和指定为optional时,则运行时不做字段非空校验。

(b). 编译并生成java源代码:

thrift -gen java pair.thrift

©. 编写序列化和反序列化的测试代码:

  • 序列化测试,将Pair对象写入文件中
private static void writeData() throws IOException, TException {
    
    Pair pair = new Pair();
    pair.setKey("key1").setValue("value1");
    FileOutputStream fos = new FileOutputStream(new File("pair.txt"));
    pair.write(new TBinaryProtocol(new TIOStreamTransport(fos)));
    fos.close();
}
  • 反序列化测试,从文件中解析生成Pair对象
private static void readData() throws TException, IOException {
    
    Pair pair = new Pair();
    FileInputStream fis = new FileInputStream(new File("pair.txt"));
    pair.read(new TBinaryProtocol(new TIOStreamTransport(fis)));
    System.out.println("key => " + pair.getKey());
    System.out.println("value => " + pair.getValue());
    fis.close();
}

(d) 观察运行结果,正常输出表明序列化反序列化过程正常完成。

Thrift协议源码

(一) writeData()分析

首先查看thrift序列化机制,即数据写入实现,这里采用二进制协议TBinaryProtocol,切入点为pair.write(TProtocol)

查看scheme()方法,决定采用元组计划(TupleScheme)还是标准计划(StandardScheme)来实现序列化,默认采用的是标准计划StandardScheme

标准计划(StandardScheme)下的write()方法:

这里完成了几步操作:

(a). 根据Thrift IDL文件中定义了required的字段验证字段是否正确赋值。

public void validate() throws org.apache.thrift.TException {
    
  // check for required fields
  if (key == null) {
    
    throw new org.apache.thrift.protocol.TProtocolException("Required field 'key' was not present! Struct: " + toString());
  }
  if (value == null) {
    
    throw new org.apache.thrift.protocol.TProtocolException("Required field 'value' was not present! Struct: " + toString());
  }
}

(b). 通过writeStructBegin()记录写入结构开始标记

public void writeStructBegin(TStruct struct) {
    }

©. 逐一写入Pair对象的各个字段,包括字段字段开始标记字段的值字段结束标记

if (struct.key != null) {
    
  oprot.writeFieldBegin(KEY_FIELD_DESC);
  oprot.writeString(struct.key);
  oprot.writeFieldEnd();
}
// 省略...

(1). 首先是字段开始标记,包括typefield-idtype是字段的数据类型的标识号field-idThrift IDL定义的字段次序,比如说key为1,value为2。

public void writeFieldBegin(TField field) throws TException {
    
  writeByte(field.type);
  writeI16(field.id);
}

Thrift提供了TType,对不同的数据类型(type)提供了唯一标识的typeID

public final class TType {
    
    public static final byte STOP   = 0; 
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值