Thrift协议介绍
本文主要说明Thrift如何序列化和反序列化数据。主要分析为什么在客户端调用method,服务端会执行对应的method,同时客户端能够获取到返回值,中间都发生了什么。
Thrift序列化的核心类是TProtocol,这是一个抽象类,主要实现有:
TBinaryProtocol:二进制协议TCompactProtocl:带压缩的二进制(其实只对i16、i32、i64这3种类型以及field的编号进行压缩,详细可以搜索zigzag编码)
本文以TBinaryProtocol为例说明,协议如下所示
1 | ---------------------------------------------------------------------------------------- |
总体我们可以分为header和body。
header首先是4个字节的magic,Thrift协议的magic是一个32位的数字,高16位是8001(个人理解代表版本号,目前是第一个版本),低16位根据TMessageType得到,目前TMessageType分为4种:
- CALL = 1 调用消息,如
0x80010001 - REPLY = 2 应答消息,如
0x80010002 - EXCEPTION = 3 异常消息,如
0x80010003 - ONEWAY = 4 单向消息,属于调用消息,但是不需要应答,如
0x80010004
之后4个字节表示方法名称的长度N,之后是N方法名称的字节,最后是4个字节的序列号。
body首先是一个struct类型,内部会根据类型的不同各不相同,同时支持不同类型的嵌套,目前支持的类型包括:
- byte
- bool
- short
- int
- long
- double
- string
- bytearray
- map
- list
- set
- field
- struct
注意:
exception可以理解为struct的一种,按照struct来序列化和反序列化。
byte占1个字节,bool也是占用1个字节,true=1,false=0。
short占2个字节,int占4个字节,long和double都是占用8个字节。
string和bytearray类似,占4+N字节,描述如下:
1 | -------------------- |
string使用UTF-8编码成byte数组。
map占1+1+4+N*X+N*Y个字节,描述如下:
1 | ---------------------------------------------------------------------------------------- |
其中key-type和value-type可以是任何以上的类型。
list和set类似,占1+4+N*X个字节,描述如下:
1 | ---------------------------------------------------------------- |
其中element-type可以是任何以上的类型。
field占1+2+X个字节,描述如下:
1 | --------------------------------------- |
field不是独立出现,而是出现在struct内部,其中field-type可以是任何以上的类型,field-id就是定义IDL时该field在struct的编号,field-value是对应类型的序列化结果。
struct占X个字节,这个不太好估计,需要具体定义具体计算,可以按照入下描述:
1 | -------------------------------- |
在使用上有些需要注意的点:
- 对于方法定义了返回值为
struct、map、set、list,如果服务端返回null,thrift是不能够很好的支持的(会报错),大家可以根据具体情况返回empty的对象或者抛出定义的异常 - 如果参数或者返回值是
map、set、list实例,该实例不能被其他线程修改,否则会报协议错误或者超时(大家可以想下是为什么)
以上基本解释了Thrift的协议格式以及不同类型的序列化格式,还有我使用过程中遇到的一些坑,希望对大家理解Thrift有所帮助。