Thrift是一个跨语言通信的服务框架,不同语言开发的程序可以通过Thrift来进行通信:
通过编译一个后缀名为.thrift的文件来生成指定语言的代码,通过生成的代码我们就可以编写出跨语言通信的代码了:如服务端是用Thrift生成的Java代码,客户端使用Thrift生成的C++/C#代码,用Thtift可以完成C++代码到Java代码的调用,而不需要关心其他如网络通信等内容,可以让开发人员专注于业务实现。
Thrift的用途
- 作为不同语言之间通信的桥梁
- 作为一套远程过程调用(RPC)框架
当然,内部的调用过程其实是一致的,都是通过序列化调用过程来实现的。
Thrift的调用过程
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();
建立一个服务端需要考虑以下几个方面的参数:
- 数据通信协议(protocol):定义传输的数据以什么格式进行通信(二进制、文本),有:TBinaryProtocol、TCompactProtocol、TJSONProtocol、TSimpleJSONProtocol、TDebugProtocol几种。
- 传输方式(transport):定义上层数据以什么方式相互传输,有:TSocket、TFramedTransport、TFileTransport、TMemoryTransport、TZlibTransport几种
- 服务端类型(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和其他方式的所产生的内容大小比较结果如下:
在上图中我们能明显看出,最臃肿的是RMI,其次是xml,使用Thrift的TCompactProtocol协议和Google 的 Protocol Buffers 相差的不算太多,相比而言还是Google 的 Protocol Buffers效果最佳。
- 使用Thrift 中的协议和其他方式的所产生的运行开销比较结果如下:
在上图中我们能明显看出,最占资源是REST2中协议,使用Thrift的TCompactProtocol协议和Google 的 Protocol Buffers 相差的不算太多,相比而言Thrift的TCompactProtocol协议效果最佳。
与Avro比较
Thrift的设计强调统一的编程接口的多语言通讯框架,而Avro偏向实用,融合了显式,declarative的Schema和高效二进制的数据表达,强调数据的自我描述,克服了以往单纯XML或二进制系统的缺陷。Avro对Schema动态加载功能。
根据之前我们介绍的,我们不难发现,如果我们需要改变调用接口的话,我们需要修改thrift文件,重新编译、生成代码,哪怕只是改变了某个方法的一个参数类型,而Avro提供了对Schema动态加载的功能,在必要时只需要修改schema,就可以动态的改变接口。
总结
Thrit作为一个跨语言通信的服务框架,与传统的REST方式调用相比,虽然灵活性稍低,但是无论性能上还是资源占用上都有具有一定优势,更重要的是她提供了一套完整的解决方案,减少了普通开发人员对细节的关注,提升了开发效率。
参考文章:
- http://thrift.apache.org/tutorial/
- http://www.haogongju.net/art/1917180
- http://www.cnblogs.com/johnc/archive/2011/06/19/2084508.html
- http://www.tbdata.org/archives/1307