thrift调用流程分析

由于工作需要使用thrift,并且需要根据需求修改thrift源码,因此必须熟悉thrift执行的流程。以下是根据thrift源码阅读而得出流程分析。

thrift协议栈概述

thrift是一个rpc框架,开发者可以通过thrift自带的接口定义语言(IDL)来自动生成客户端和服务端的rpc代码。
thrift协议栈如下图所示:

thrift调用流程分析

在client和server的最顶层都是用户自定义的处理逻辑,也就是说用户只需要编写用户逻辑,就可以完成整套的rpc调用流程。用户逻辑的下一层是thrift自动生成的代码,这些代码主要用于结构化数据的解析、发送和接收,同时服务端的自动生成代码中还包含了rpc请求的转发(client的A调用转发到server A函数进行处理)。

从上面可以看出thrift的模块是分层设计的,在每一个层次可以根据业务的实际需要选择合适的实现方式。

thrift主要分为以下几种层次模块:

  • 底层io模块,负责实际的数据传输,包括socket、文件或压缩数据流等。
  • transport层负责以字节流方式发送和接收消息,是底层io模块在thrift框架中的实现,每一个底层io模块都会有一个对于TTransport来负责thrift的字节流(byte stream)数据在该io模块上的传输。例如,TSocket对应socket传输,TFileTransport对应文件传输。
  • TProtocol主要负责结构化数据组装成消息,或者从消息结构中读出结构化数据。TProtocol将一个有类型的数据转化为特定类型的数据。如int32会被TBinaryProtocol编码为一个4字节数据,或TBinaryProtocol从TTransport中取出4个字节数据解码为int32。
  • TServer负责接收client请求,并将请求转发到processor进行处理。TServer主要任务是高效地接受client的请求,特别是高并发请求的情况下快速完成请求。
  • processor负责对client的请求进行响应,包括rpc请求转发,调用参数解析和用户逻辑调用,返回值写回等处理步骤。processor是服务端从thrift框架转入用户逻辑的关键流程。processor同时也负责向消息结构中写入数据或读出数据。

TServer

thrift核心库提供了一个TServer抽象类。

TServer在thrift框架中的主要任务是接收client请求,并转发到某个processor上进行请求处理。针对不同的访问规模,thrift提供了不同TServer模型。thrift目前支持的server模型包括:

  • TSimpleServer:使用阻塞io的单线程服务器,主要用于调试。
  • TThreadedServer:使用阻塞io的多线程服务器,每一个请求都在一个线程中处理,并发访问情况下会有很多线程同时运行。
  • TThreadPoolServer:使用阻塞io的多线程服务器,使用线程池管理处理线程。
  • TNonBlockingServer:使用非阻塞io的多线程服务器,使用少量线程既可以完成大并发量的请求响应,必须使用TFramedTransport。

TServer对象通常如下工作:

  1. 使用TServerTransport获得一个TTransport。
  2. 使用TTransportFactory,可选地将原始传输转换为一个合适的应用传输。
  3. 调用TProtocolFactory,为TTransport创建一个输入和输出。
  4. 调用TProcessor对象的process方法。

TTransport

TTransport是与底层数据传输紧密相关的传输层。每一种支持的底层传输方式都存在一个与之对应的TTransport。在这一层,数据是按字节流处理的,即传输层看到的是一个又一个的字节,并把这些字节按顺序发送和接收。TTransport并不了解它所传输的数据是什么类型,实际上传输层也不关心数据是什么类型,只需要按照字节方式对数据进行发送和接收即可。数据类型的解析在TProtocol这一层完成。

TTransport具体的有以下几个类:

  • TSocket:使用阻塞的TCP socket进行数据传输,也是最常见的模式。
  • THttpTransport:采用http传输协议进行数据传输。
  • TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。
  • TZlibTransport:与其他transport配合使用,压缩后对数据进行传输,或者将收到的数据解压。

TProtocol

TProtocol的主要任务是把TTransport中的字节流转换为数据流。在TProtocol这一层就会出现具有数据类型的数据,如整型、浮点数、字符串和结构体等。TProtocol中数据虽然有了数据类型,但TProtocol只会按照指定类型将数据读出和写入,而对于数据的真正用途,需要在thrift自动生成的server和client中处理。

thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本和二进制传输协议,以节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为大多数。常用协议有以下几种:

  • TBinaryProtocl:二进制编码格式
  • TCompactProtocol:高效率的、密集的二进制编码格式
  • TJSONProtocol:使用JSON的数据编码协议进行数据传输
  • TSimpleJSONProtocol:提供JSON只写协议,生成的文件很容易通过脚本语言解析
  • TDebugProtocol:简单易懂的文本格式,以便于debug

TProcessor

TProcessor主要对TServer中一次请求的inputProtocol和outputProtocol进行操作,也就是从inputProtocol中读出client的请求数据,向outputProtocol写入用户逻辑的返回值。TProcessorprocess是一个非常关键的处理函数,因为client所有的rpc调用都会经过该函数处理并转发。

TProcessor对一次rpc调用的处理流程可以概括为:

  1. TServer接收到rpc请求之后,调用TProcessorprocess进行处理。
  2. TProcessorprocess首先调用TTransport.readMessageBegin接口,读出rpc调用的名称和rpc调用类型。如果rpc调用类型是rpc call,则调用TProcessor.process_fn继续处理,对于未知的rpc调用类型,则抛出异常。
  3. TProcessor.process_fn根据rpc调用名称,到自己的processMap中查找对应的rpc处理函数。如果存在对应的rpc处理函数,则调用该处理函数继续进行请求响应。不存在则抛出异常。

而rpc处理函数是rpc请求的最终步骤,主要有以下三个过程:

  1. 调用rpc请求参数的解析类,从TProtocol中读入数据完成参数解析。不管rpc调用的参数有多少个,thrift都会将参数放到一个结构体中。thrift会检查读出参数的字段id和字段类型是否与要求的参数匹配。对于不符合要求的参数都会跳过。这样,rpc接口发生变化之后,旧的处理函数在不做修改的情况下,可以通过跳过不认识的参数,来继续提供服务。
  2. 参数解析完后,调用用户逻辑,完成真正的请求响应。
  3. 用户逻辑的返回值使用返回值打包类进行打包,写入TProtocol。

ThriftClient

ThriftClient跟TProcessor一样主要操作inputProtocol和outputProtocol,不同的是thriftClient将rpc调用分为send和receive两个步骤:

  1. send步骤,将用户的调用参数作为一个整体的struct写入TProtocol,并发送到TServer。
  2. send结束后,thriftClient便立即进入receive状态等待TServer的响应。对于TServer的响应,使用返回值解析类进行返回值解析,完成rpc调用。

相关推荐