RPC-Thrift 协议

简介

        Thrift是Facebook于2007年开发的跨语言的rpc服框架,提供多语言的编译功能,并提供多种服务器工作模式;用户通过Thrift的IDL(接口定义语言)来描述接口函数及数据类型,然后通过Thrift的编译环境生成各种语言类型的接口文件,用户可以根据自己的需要采用不同的语言开发客户端代码和服务器端代码。

一、序列化协议

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

序列化协议类型:

  1. TBinaryProtocol:二进制编码格式进行数据传输。
  2. TCompactProtocol:高效密集型的二进制序列化协议,使用Variable-Length Quantity (VLQ) 编码对数据进行压缩。
  3. TJSONProtocol:使用JSON的数据编码协议进行数据传输。
  4. TSimpleJSONProtocol:这种节约只提供JSON只写的协议,适用于通过脚本语言解析。
  5. TTupleProtocol(继承自TCompactProtocol)
  6. TDebugProtocol:在开发的过程中帮助开发人员调试用的,以文本的形式展现方便阅读。

RPC框架中一般使用 TCompactProtocol

 

二、传输层

Thrift中所有的传输层协议的基类是TTransport。另外,需要说明的一点是,thrift是基于TCP协议的。

传输协议类型:

  1. TSocket:使用堵塞式I/O进行传输,也是最常见的模式。
  2. TFramedTransport:使用非阻塞方式,以frame为单位进行传输,类似于Java中的NIO。
  3. TFileTransport:以文件形式进行传输,虽然这种方式不提供Java的实现,但是实现起来非常简单。
  4. TMemoryTransport:使用内存I/O,如Java中的ByteArrayOutputStream实现。
  5. TZlibTransport:使用执行zlib压缩,与其他传输方式联合使用,不提供Java的实现。
  6. TNonblockingTransport:使用非阻塞方式,用于构建异步客户端。

RPC框架中一般使用 TFramedTransport

 

三、Thrift的序列化和反序列化方式

步骤:

  1. 使用IDL创建thrift接口定义文件;
  2. 将thrift的定义文件转换为对应语言的源代码;
  3. 选择相应的protocol,进行序列化和反序列化;

 

四、TCompactProtocol与TBinaryProtocol的原理和区别

Thrift二进制序列化协议中,默认为TBinaryProtocol,关于TCompactProtocol的说明,为高效密集型的二进制序列化(varint)。

那么TCompactProtocol相对于TBinaryProtocol是怎样做到高效密集的呢?TCompactProtocol是否一定比TBinaryProtocol高效?

我们以比较常用的i32类型为例,来解释一下两种方式各自的原理:

TBinaryProtocol

处理i32整型数据类型时,定义的是4个字节的数组,32位的长度正好可以保存到这4个字节组当中。如果我们分别以n1~n32来表示第1位到第32位,那么这个数组的数据结构应该为以下结构:

i32out[0] {n1  ~ n8 }
i32out[1] {n9  ~ n16}
i32out[2] {n17 ~ n24}
i32out[3] {n25 ~ n32}

这样的实现很简单.

对于其它类型,比如i16,也是类似的原理,不过是以2个字节的数组保存,在此不再说明了。

因为我们架构中使用的是TCompactProtocol,所以我们需要重点了解一下该协议的序列化方式。

TCompactProtocol

在处理i32整型数据类型时,与TBinaryProtocol完全不同,采用的是1~5个字节组来保存。依然以n1~n32来表示第1位到第32位,数据结构应该为以下结构:

i32out[0] {1 , 0 , 0 , 0 , n1 ~ n4}
i32out[1] {1 , n5 ~ n11}
i32out[2] {1 , n12 ~ n18}
i32out[3] {1 , n19  ~ n25}
i32out[4] {0 , n26  ~ n32}

这是一种极端情况,5个字节全部占满。

很显然,这样做比TBinaryProtocol复杂得多,而且还多了1个字节,并没有达到密集的目的。那是不是说明TBinaryProtocol更好?

先不急着下结论,举个具体一点的例子来说明两种实现的区别。

假如我们需要序列化一个十进制数值'10',那么它的二进制表示方式应该为'1010',只用了4位,但i32会在前面自动补0,

则是:'00000000000000000000000000001010',那么如果使用TBinaryProtocol方式来保存,则应该为以下结构:

i32out[0] {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0}
i32out[1] {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0}
i32out[2] {0 , 0 , 0 , 0 , 0 , 0 , 0 , 0}
i32out[3] {0 , 0 , 0 , 0 , 1 , 0 , 1 , 0}

这样有什么问题呢?那就是大量补足的0占用了宝贵空间。

接着我们再来看看TCompactProtocol会怎样保存'10'这个数值:

i32out[0] {0 , 0 , 0 , 0 , 1 , 0 , 1 , 0}

TCompactProtocol只用了1个字节,而TBinaryProtocol依然用了4个字节。这就是为什么说TCompactProtocol高效密集型的二进制序列化的原因。

TCompactProtocol的保存规则

TCompactProtocol每个字节的第1位是状态位,第2位到第8位保存具体的数据.

这有别于TBinaryProtocol的1到8位全部保存具体数据。这也是为什么极端情况下TCompactProtocol比TBinaryProtocol多占1个字节的原因。

TCompactProtocol的字节中第1位状态位的意思是标记此字节后是否还有数据。1为有数据,0为没有数据.

为了更容易理解,我们再举一个例子,用TCompactProtocol来序列化十进制数值'300',二进制应该为'100101100',用TCompactProtocol方式来保存则为如下结构:

i32out[0] {1 , 0 , 0 , 0 , 0 , 0 , 1 , 0}
i32out[1] {0 , 0 , 1 , 0 , 1 , 1 , 0 , 0}

这里将'100101100'拆分为了2部分,'10'和'0101100',在i32out[0]中保存了'10',并在第1位记为'1'来表示后面还有数据,第7位和第8位保存'10',不足的几位(第2位到第6位)补0;

所以i32out[0]为'10000010',在i32out[1]中保存了后面的'0101100',并在第1位记为'0'来表示后面没有了,则第1位为'0',所以i32out[1]为'00101100'。

TCompactProtocol以这样的原理来达到压缩的目的。

五、数据交换格式分类

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

1. 自解析型

        序列化的数据包含完整的结构,包含了field名称和value值. 比如xml/json/java serizable,百度的mcpack/compack, 都属于此类. 即调整不同属性的顺序对序列化/反序列化不影响。
2. 半解析型

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

3. 无解析型

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

thrift 文件:

namespace java mmxf.thrift;
 
struct Pair {
  1: required string key
  2: required string value
}

"1", "2" 这些数字标识符究竟有何含义? 它在序列化机制中究竟扮演什么样的角色?

thrift官网描述:thrift的向后兼容性(Version)借助属性标识(数字编号id + 属性类型type)来实现, 可以理解为在序列化后(属性数据存储由 field_name:field_value => id+type:field_value)。

所以,id很重要,一旦id顺序混淆或者有变化,value值与name的对应也会变换,name在其中并没有映射作用。

注意:RPC服务数据发送方(生产者)和读取方(消费者)如果同样的字段name,id不同,获取到的value是不一致的,会出现不同name的value互换的奇怪现象。

发布了14 篇原创文章 · 获赞 11 · 访问量 9329

猜你喜欢

转载自blog.csdn.net/qq_27641935/article/details/94563324