Thrift简介(一个跨语言通信框架)

Thrift是一个跨语言通信的服务框架,不同语言开发的程序可以通过Thrift来进行通信:

通过编译一个后缀名为.thrift的文件来生成指定语言的代码,通过生成的代码我们就可以编写出跨语言通信的代码了:如服务端是用Thrift生成的Java代码,客户端使用Thrift生成的C++/C#代码,用Thtift可以完成C++代码到Java代码的调用,而不需要关心其他如网络通信等内容,可以让开发人员专注于业务实现。

Thrift的用途

  1. 作为不同语言之间通信的桥梁
  2. 作为一套远程过程调用(RPC)框架

当然,内部的调用过程其实是一致的,都是通过序列化调用过程来实现的。

Thrift的调用过程

Apache-Thrift-Architecture

Thirft通过Generated Code来完成整个调用过程。

如何开始

  • Thrift IDL file

上文提到有后缀名为.thrift的文件,名为Thrift IDL file,此文件描述了相关接口规范,这也与接口描述语言(Interface description language,IDL)相对应。Thrift实现了一套自己的IDL,并通过其提供的编译器来生成代码。

下面看一下一个thrift文件中都包含什么:

include "SearchRequest.thrift"
include "SearchResponse.thrift"
namespace java com.surfilter.dse.common.thrift.master.rpc

service TMaster {
        SearchResponse.TSearchResponse submitSearch(1:SearchRequest.TSearchRequest searchRequest);

        void cancelSearch(1:string planId);
}

一个thrift文件可以包含以下几种类型的数据:

* Base Types:基本类型
* Struct:结构体类型
* Container:容器类型,即List、Set、Map
* Exception:异常类型
* Service: 定义对象的接口,和一系列方法

像定义普通的接口一样,可以很容易的编写一个thrift文件,只需要注意相应的语法即可,如i32对应Java中的Interget类型,string对应String类型等,具体的可参考apache官方文档:http://thrift.apache.org/docs/types/

  • Generate Code

有了thrift文件就可以生成代码了,Thrift提供win平台及linux平台的代码生成工具,其中linux下需要自己编译生成Apache Thrift compiler,win平台有已经生成好的compiler的可执行文件,可在:http://thrift.apache.org/download/下载。

thrift --gen <language> <Thrift filename>

就这么简单,只需要指定生成代码的目标语言与thrift文件就可以生成代码了。

  • 服务端编程

得到了生成的代码后,将代码导入自己的工程中就可以使用了,下面通过一个简单的例子来说明如何使用Thrift,语言采用java

//server socket
TServerSocket serverTransport = new TServerSocket(1234);

//继承自生成代码接口的业务处理类
IMaster masterHandler = new MasterHandler(null);

//A processor is a generic object which operates upon an
//input stream and writes to some output stream.
TProcessor processor = new TMaster.Processor(masterHandler);

//protocol,数据通讯协议,定义数据格式
TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(true, true);

//transport、定义传输方式
TTransportFactory transportFactory = new TFramedTransport.Factory();

Args args = new Args(serverTransport);
args.processor(processor);
args.protocolFactory(protocolFactory);
args.transportFactory(transportFactory);

//服务端对象
TServer server = new TThreadPoolServer(args);

//启动服务
server.serve();

建立一个服务端需要考虑以下几个方面的参数:

  1. 数据通信协议(protocol):定义传输的数据以什么格式进行通信(二进制、文本),有:TBinaryProtocol、TCompactProtocol、TJSONProtocol、TSimpleJSONProtocol、TDebugProtocol几种。
  2. 传输方式(transport):定义上层数据以什么方式相互传输,有:TSocket、TFramedTransport、TFileTransport、TMemoryTransport、TZlibTransport几种
  3. 服务端类型(Server Type):定义服务端以什么方式提供服务,有TSimpleServer、TThreadPoolServer、TNonblockingServer几种

注意:以上并不是所有都提供Java实现的,使用需要注意。另外根据上边的图片所示,TTransport是在TProtocol下层。

  • 客户端编程

下面给出Java版的客户端调用方式:

//定义传输方式
TTransport transport = new TSocket("localhost", 1234);
//打开传输端口
transport.open();
//定义数据通信协议
TProtocol protocol = new TBinaryProtocol(transport);
//构建thrift client
Test.Client client = new Test.Client(protocol);
//调用相应方法
client.doSomthing();

由上述代码可以看出,客户端代码只需要规定传输方式及数据通信格式即可。

与其他类似技术的比较

其实有很多技术能支撑远程调用,如常见的REST-JSON方式、REST-XML或RMI等,但REST方式的效率上确实不够高,下面引用了几张图片,可以直观的说明一些问题。

    • 使用Thrift和其他方式的所产生的内容大小比较结果如下:

thrift-size

在上图中我们能明显看出,最臃肿的是RMI,其次是xml,使用Thrift的TCompactProtocol协议和Google 的 Protocol Buffers 相差的不算太多,相比而言还是Google 的 Protocol Buffers效果最佳。

  • 使用Thrift 中的协议和其他方式的所产生的运行开销比较结果如下:

thrift-load

在上图中我们能明显看出,最占资源是REST2中协议,使用Thrift的TCompactProtocol协议和Google 的 Protocol Buffers 相差的不算太多,相比而言Thrift的TCompactProtocol协议效果最佳。

与Avro比较

Thrift的设计强调统一的编程接口的多语言通讯框架,而Avro偏向实用,融合了显式,declarative的Schema和高效二进制的数据表达,强调数据的自我描述,克服了以往单纯XML或二进制系统的缺陷。Avro对Schema动态加载功能。

根据之前我们介绍的,我们不难发现,如果我们需要改变调用接口的话,我们需要修改thrift文件,重新编译、生成代码,哪怕只是改变了某个方法的一个参数类型,而Avro提供了对Schema动态加载的功能,在必要时只需要修改schema,就可以动态的改变接口。

总结

Thrit作为一个跨语言通信的服务框架,与传统的REST方式调用相比,虽然灵活性稍低,但是无论性能上还是资源占用上都有具有一定优势,更重要的是她提供了一套完整的解决方案,减少了普通开发人员对细节的关注,提升了开发效率。

参考文章:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注