Thrift实现原理

 

 

Thrift实现原理

 RPC框架是什么

RPC,即 Remote Procedure Call(远程过程调用),通俗点说就是:调用远程计算机上的服务,就像调用本地服务一样。

两个服务A和B,部署在不同的服务器上,由于在不同服务器,所以,A服务想要调用B服务,是没法直接进行调用的,那么就需要用网络来表达调用的语义和传达调用的数据。

如何做呢?主要需要解决如下几个问题:

  1. 解决通讯的问题,一般来说都是建立服务器与客户端的TCP连接或HTTP连接,所有服务器与客户端的数据交互都是在这个连接里进行,TCP 是传输层协议,HTTP 是应用层协议,而传输层较应用层更加底层,在数据传输方面,越底层越快,因此,在一般情况下,TCP 一定比 HTTP 快,像早期的Web Service 就是基于 HTTP 协议的 RPC,它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC。

  2. 解决服务发现的问题,A服务如何告诉B服务它提供了什么样的能力,一般都会有注册中心等做服务发现,目前流行的都是使用zookeeper做注册中心进行服务发现。

  3. 解决传输问题,服务调用需要将底层数据通过TCP或HTTP进行传输,那么如何高效的序列化以及反序列化就是需要解决的问题,市面上有很多优秀的序列化框架,比如:Protobuf、Kryo、Hessian、Jackson 等,它们可以取代 Java 默认的序列化,从而提供更高效的性能

目前流行RPC框架有很多,从最早的CORBA,Java RMI到Web Service的RPC,Hessian,Dubbo,Thrift,这些RPC框架都是为了能够提供出一套快捷,方便的服务调用。我们这里针对Thrift的一套实现来看一下它是如何做到解决上述问题的。

Thrift架构

Thrift实现原理

 

如图:

  1. 黄色部分是用户实现的业务逻辑。

  2. 褐色部分是根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架。

    Processor类,该类主要是开发Thrift服务器程序的时候使用,该类内部定义了一个map,它保存了所有函数名到函数对象的映射,一旦Thrift接到一个函数调用请求,就从该map中根据函数名字找到该函数的函数对象,然后执行它。

  3. 红色部分是根据 Thrift 文件生成代码实现数据的读写操作。

  4. TProtocol传输协议,传输协议是用来描述what is transmitted。

    Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本 (text) 和二进制 (binary) 传输协议,为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议。不同协议的实现体现在传输数据上是这样的,例如实现了TProtocol接口的TBinaryProtocol类,对于readDouble()函数就是按照二进制的方式读取出一个Double类型的数据。同理,写入的时候,也会按照对应的协议进行写入操作。

    目前Thrift支持的协议有这些种:

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

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

    • TJSONProtocol —— 使用 JSON 的数据编码协议进行数据传输

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

       

Thrift实现原理

  1. TTransport传输层,传输层是用来描述how to transmitted

    传输层实际上可以理解为对I/O层操作的一个封装,更直观的理解为它封装了一个socket,不同的实现类有不同的封装方式,常见的一般为阻塞式,同步非阻塞式和异步非阻塞,目前支持的有如下几种:

    • TFramedTransport 使用非阻塞方式,按块的大小进行传输

    • TSocket 使用阻塞式IO进行传输

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

Thrift时序图

看完上述服务架构图之后,我们分别看下Thrift的server端和client端的时序图。

server端时序图

Thrift实现原理

 

client时序图

Thrift实现原理

 

Thirft为何比HTTP等请求快

对Thrift有了整体的了解之后,我们从如下几个方面分析一下为何Thrift为何比HTTP等其他RPC框架快

序列化之后的数据大小

Thrift提供了几种序列化协议,我们对比Thrift与其他RPC框架的序列化协议序列化之后字节流的大小可以发现,Thrift序列化后的大小和Protocol Buffers差不多,比其他的都会小很多的,那么这样一来,传输过程中使用的时间肯定会缩短。

下图是各个序列化协议对同样的内容进行序列化后的大小对比图:

Thrift实现原理

 

序列化的耗时及cpu的占用情况

下图可以看到,针对同样大小的文件,进行多次序列化,拿出的一个对比图,Thrift的序列化时间是很短的,同时也可以看出二进制的传输协议是比基于文本的传输协议性能要好很多的。

Thrift实现原理

 

使用TCP协议进行传输

我们知道网络模型中的七层分层,HTTP等协议是位于第七层应用层的协议,那么通过HTTP进行连接的时候,必然要经由下面六层才能进行传输,而TCP本身是位于第四层传输层的协议,天然就不用经由上面三层的传输,故这里的耗时也会减少一部分。

 

Thrift实现原理

 

 

Thrift实现原理

 

使用非阻塞I/O的形式处理并发量高的请求

从时序图中我们看到,server端本身是有使用线程池的,这样是为了能同时提供多个连接对外服务。

同时,Thrift的server端还提供了异步非阻塞I/O(异步非阻塞I/O后续再做总结)的server场景,不需要占用链接,等响应之后,找到之前的链接在反馈给客户端,这样一来能大大的提高并发。

小结

以上就是Thrift的一套基本的架构及基本的架构原理。素材摘自不同的地方,有想要在更深入理解的,可以下载Thrift源码看下整个结构树。

参考资料

http://jnb.ociweb.com/jnb/jnbJun2009.html

https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/

https://blog.****.net/houjixin/article/details/42779835

http://www.importnew.com/20327.html