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

前言

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

正文

数据交换格式简介

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

(一) 自解析型

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

(二) 半解析型

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

(三) 无解析型

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

Thrift的数据类型

基本类型:

bool: 布尔值

byte: 8位有符号整数

i16: 16位有符号整数

i32: 32位有符号整数

i64: 64位有符号整数

double: 64位浮点数

string: UTF-8编码的字符串

binary: 二进制串

结构体类型:

struct: 定义的结构体对象

容器类型:

list: 有序元素列表

set: 无序无重复元素集合

map: 有序的key/value集合

异常类型:

exception: 异常类型

服务类型:

service: 具体对应服务的类

Thrift的序列化协议

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

TBinaryProtocol二进制编码格式进行数据传输

TCompactProtocol高效率的、密集二进制编码格式进行数据传输

TJSONProtocol: 使用文本的数据编码协议进行数据传输

TSimpleJSONProtocol:只提供只写的协议,适用于通过脚本语言解析

Thrift的序列化测试

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

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

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

(c). 编写序列化和反序列化的测试代码:

序列化测试,将 对象写入文件中

反序列化测试,从文件中解析生成 对象

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

Thrift协议源码

(一) writeData()分析

首先查看 的序列化机制,即数据写入实现,这里采用二进制协议,切入点为 :

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

标准计划( )下的 方法:

这里完成了几步操作:

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

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

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

(1). 首先是字段开始标记,包括 和 。 是字段的数据类型的标识号, 是 定义的字段次序,比如说 为1, 为2。

提供了 ,对不同的数据类型( )提供了唯一标识的 。

(2). 然后是写入字段的值,根据字段的数据类型又归纳为以下实现: 、 、 、 、 、 和 方法。

通过一个长度为 的 字节数组缓存写入读取的临时字节数据。

常识1:16进制的介绍。以0x开始的数据表示16进制,0xff换成十进制为255。在16进制中,A、B、C、D、E、F这五个字母来分别表示10、11、12、13、14、15。

进制十进制:f表示15。第n位的权值为16的n次方,由右到左从0位起:0xff = 1516^1 + 1516^0 = 255进制二进制再变十进制:0xff = 1111 1111 = 2^8 - 1 = 255

常识2:位运算符的使用。>>表示代表右移符号,如:int i=15; i>>2的结果是3,移出的部分将被抛弃。而>刚好相反。

转为二进制的形式可能更好理解,0000 1111(15)右移2位的结果是0000 0011(3),0001 1010(18)右移3位的结果是0000 0011(3)。

writeByte():写入单个字节数据。

writeBool():写入布尔值数据。

writeI16():写入短整型类型数据。

writeI32():写入整型类型数据。

writeI64():写入长整型类型数据。

writeDouble():写入双浮点型类型数据。

writeString():写入字符串类型,这里先写入字符串长度,再写入字符串内容

writeBinary:写入二进制数组类型数据,这里数据输入是 中的 类型。

(3). 每个字段写入完成后,都需要记录字段结束标记

(d). 当所有的字段都写入以后,需要记录字段停止标记

(e). 当所有数据写入完成后,通过 记录写入结构完成标记

(二) readData()分析

查看 的反序列化机制,即数据读取实现,同样采用二进制协议,切入点为 :

数据读取数据写入一样,也是采用的标准计划标准计划( )下的 方法:

这里完成的几步操作:

(a). 通过 读取结构开始标记

(b). 循环读取结构中的所有字段数据到 对象中,直到读取到 为止。 指明开始读取下一个字段的前需要读取字段开始标记

(c). 根据 定义的 读取对应的字段,并赋值到 对象中,并设置 对象相应的字段为已读状态(前提:字段在 中被定义为 )。

关于读取字段的值,根据字段的数据类型也分为以下实现: 、 、 、 、 、 和 方法。

readByte():读取单个字节数据。

readBool():读取布尔值数据。

readI16():读取短整型类型数据。

readI32():读取整型类型数据。

readI64():读取长整型类型数据。

readDouble():读取双精度浮点类型数据。

readString():读取字符串类型的数据,首先读取并校验 字节的字符串长度,然后检查缓冲区中是否有对应长度的字节未消费。如果有,直接从缓冲区中读取;否则,从传输通道中读取数据。

如果是从传输通道中读取数据,查看 方法:

readBinary():读取二进制数组类型数据,和字符串读取类似,返回一个 字节缓存对象。

(d). 每个字段数据读取完成后,都需要再读取一个字段结束标记

(e). 当所有字段读取完成后,需要通过 再读入一个结构完成标记

(f). 读取结束后,同样需要校验在 中定义为 的字段是否为空,是否合法。

总结

其实到这里,对于 的序列化机制反序列化机制具体实现高效性,相信各位已经有了比较深入的认识!

欢迎关注技术公众号: 零壹技术栈

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180530G09AO000?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券