RPC-Thrift协议

一、序列化协议

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

序列化协议类型:

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

TCompactProtocol:高效密集型的二进制序列化协议,使用Variable-Length Quantity (VLQ) 编码对数据进行压缩。

TJSONProtocol:使用JSON的数据编码协议进行数据传输。

TSimpleJSONProtocol:这种节约只提供JSON只写的协议,适用于通过脚本语言解析。

TTupleProtocol(继承自TCompactProtocol)

TDebugProtocol:在开发的过程中帮助开发人员调试用的,以文本的形式展现方便阅读。

RPC框架中一般使用 TCompactProtocol。

二、传输层

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

传输协议类型:

TSocket:使用堵塞式I/O进行传输,也是最常见的模式。

TFramedTransport:使用非阻塞方式,以frame为单位进行传输,类似于Java中的NIO。

TFileTransport:以文件形式进行传输,虽然这种方式不提供Java的实现,但是实现起来非常简单。

TMemoryTransport:使用内存I/O,如Java中的ByteArrayOutputStream实现。

TZlibTransport:使用执行zlib压缩,与其他传输方式联合使用,不提供Java的实现。

TNonblockingTransport:使用非阻塞方式,用于构建异步客户端。

RPC框架中一般使用 TFramedTransport。

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

步骤:

使用IDL创建thrift接口定义文件;

将thrift的定义文件转换为对应语言的源代码;

选择相应的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以这样的原理来达到压缩的目的。


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互换的奇怪现象。

数据交换格式分类

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

1. 自解析型

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

2. 半解析型

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

3. 无解析型

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342