Thrift简介及实例应用

来源:互联网 发布:开源 大数据连接器 编辑:程序博客网 时间:2024/04/28 13:17

最近Boss让我研究一下Thrift,所以通过查阅大量的资料,整理出以下文档,在此备份。

一、引言

1、 背景

Thrift源于facebook,2007年facebook提交Apache基金会将Thrift作为一个开源项目。Facebook创造Thrift的目的是为了解决facebook系统中各系统间大数据量的传输通信以及系统间语言环境不同需要跨平台的问题。

2、 简介

Apache Thrift是Facebook实现的一种高效的、支持多种编程语言(IDL)的远程服务调用框架,包括一个软件库和一组代码生成工具,以加快高效率、可扩展的后端服务的开发与实现的速度。它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,它的代码生成器引擎可以在多种语言中,如C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、Smalltalk等创建高效的、无缝的服务,传输数据格式采用二进制格式,相对于JSON和XML体积更小,对于高并发、大数据量和多语言的环境更有优势。

它把各语言中最常用的部分加以抽象,把它们放进一个通用库里,再用各个语言实现,来实现跨编程语言的高效而可靠的通信。亦即,Thrift允许开发者在一个单独的语言无关的文件里(IDL文件),定义数据类型和服务接口,然后生成用来构建RPC客户和服务器所需的全部代码。

Thrift服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器/容器一起运行,可以和现有的J2EE服务器/WEB容器无缝结合。

跨语言支持:Thrift可以通过代码生成工具将接口定义文件生成不同语言的服务器端和客户端代码,从而实现服务器端和客户端的跨语言支持。用户在thrift定义文件中声明服务,这些服务通过编译生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提供服务)。其中Protocol(协议层,定义数据的传输格式)和Transport(传输层,定义数据传输方式)被用作运行时库

3、 特点

优点:

1、  支持多种编程语言:C++、Java、Python、PHP、Ruby、Erlang、Perl、Haskell、C#、Cocoa、Smalltalk;

2、  在多种不同的语言之间通信,thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务;

3、  Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。

缺点:Thrift适用于程序对程序静态的数据交换,需要先确定好他的数据结构,它是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程。

4、  开发流程

定义IDL文件--->使用代码生成工具生成代码---->实现类接口---->实现客户端以及服务器端。

二、技术要领

1、 整体架构

Thrift包含了一个完整的堆栈结构来构建客户端和服务器,如下图所示:


顶部的黄色部分是在Thrift 定义文件中,用户实现的业务逻辑代码;褐色部分是根据Thrift定义的服务接口描述文件生成的客户端和服务器端代码框架;红色部分是根据Thrift文件生成代码实现数据的读写操作;红色部分以下是Thrift的协议、传输体系以及底层I/O通信,使用Thrift可以很方便的定义一个服务并且选择不同的传输协议和传输层而不用重新生成代码。

2、 类型

Thrift提供各种语言中最基本的类型,是一种通用的类型系统。Thrift类型系统的目标是使编程者能使用完全在Thrift中定义的类型,而不论他们使用的是哪种编程语言。

Thrift类型系统没有引入任何特殊的动态类型或包装器对象,也不要求开发者编写任何对象序列化或传输的代码。

Base Types:基本类型

Struct:结构体类型

Container:容器类型,即List、Set、Map

Exception:异常类型

Service:定义对象的接口,和一系列方法

 

1、  基本类型

1.1、       bool:布尔值,true或false;

1.2、       byte:8位有符号整数;

1.3、       i16:16位有符号整数;

1.4、       i32:32位有符号整数;

1.5、       i64:64位有符号整数;

1.6、       double:64位浮点数;

1.7、       string:未知编码的文本或二进制字符串。

2、  结构体类型

Thrift结构体定义了可以被不同语言使用的公共对象,类似于C语言中的结构体定义。

3、  容器类型

3.1、       list<type>:一系列type类型的元素组成的有序表,类似于C++ STL中的vector,Java的ArrayList,元素可以重复;

3.2、       set<type>:一系列type类型的元素组成的无序表,类似于C++ STL中的set,Java中的HashSet,Python中的set,元素唯一;

3.3、       map<type1,type2>:一个严格且唯一的键值映射,类似于C++ STL中的map,Java中的HashMap。

4、  异常类型

类似Java中的Exception。

5、  服务类型

服务类型使用Thrift类型进行定义,如同面向对象编程中接口的定义,定义规则如下:

service <name> {

<returntype><name> (<arguments>)

        [throws(<exceptions>)]

...

}

 

3、 传输

各个语言必须有一种双向传输原始数据的通用接口,传输层封装了数据的传输细节。一个给定的传输是如何实现的,应该与服务开发者无关。

1) 接口

Thrift实现中,一个关键的设计选择就是将传输层从代码生成层解耦。从根本上,生成的Thrift代码只需要知道如何读和写数据。数据的源和目的地无关紧要,可以使一个socket,一段共享内存,或本地磁盘上的一个文件。TTransport(Thrift transport)接口用来读、写、传输数据,它支持以下方法:

open   Opens the tranpsort

close   Closes the tranport

isOpen Indicates whether the transport is open

read   Reads from the transport

write  Writes to the transport

flush  Forces any pending writes

除以上的TTransport接口外,还有一个TServerTransport接口,用来接受或创建原始传输对象。它的接口如下:

open   Opens the transport

listen   Begins listening for connections

accept  Returns a new client transport

close   Closes the transport

2) 实现

常用的传输层有以下几种:

1、  TSocket:使用阻塞式I/O进行传输,是最常见的模式;

2、  TFramedTransport:使用非阻塞方式,按块的大小进行传输,要求服务器为非阻塞类型;

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

4、 协议

Thrift中第二个主要的抽象,是数据结构与传输表现的分离。传输数据时,Thrift强制一个特定的消息结构,但所使用的协议编码对它来说是不可知的。换句话说,只要数据支持一组固定的操作,使它能够被生成的代码确定地读写,那么无论数据编码使用的是XML,ASCII或者二进制格式,都无关紧要。

实现:

Thrift 传输协议总体上划分为文本(text)和二进制(binary)传输协议,为了节约带宽,提高传输效率,一般采用二进制传输协议。常用的协议有如下几种:

1、  TBinaryProtocol:以二进制编码格式进行数据传输;

使用方法:Factory proFactory = newTBinaryProtocol.Factory();

TProtocol protocol = newTBinaryProtocol(transport);

2、  TDenseProtocoal:它和TCompactProtocol协议相似,但是省略了传送数据的元信息,增加了接收器,目前正在试验阶段,不能在java中使用

3、  TCompactProtocol:以高效率的、密集的二进制编码格式进行传输;

使用方法:TCompactProtocol.FactoryproFactory = new TCompactProtocol.Factory();

TCompactProtocol protocol = newTCompactProtocol(transport);

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

使用方法:TJSONProtocol.FactoryproFactory = new TJSONProtocol.Factory();

TJSONProtocol protocol = newTJSONProtocol(transport);

5、  TSimpleJSONProtocol:只提供JSON只写的协议,生成的文件适用于通过脚本语言解析。

6、  TDebugProtocol:以一种可读的文本格式帮助调试。

5、 处理器

处理数据流,完成RPC。Processor封装了从输入数据流中读取数据向输出数据流中写

数据的操作。读写数据流用Protocol对象表示。与服务相关的processor实现由编译器产生。Processor主要工作流程为:从连接中读取数据(使用Protocol),将处理授权给接口实现Handler(由用户实现),最后将结果写到连接上(使用输出Protocol)。

Processor的结构体非常简单:

interfaceTProcessor {

bool process(TProtocol in, TProtocol out)throws TException

}

6、 服务端

常见的服务端类型有以下几种:

1、  TSimpleServer:单线程服务器,使用标准的阻塞式I/O;

2、  TThreadPoolServer:多线程服务器,使用标准的阻塞式I/O;

3、  TNonblockingServer:多线程服务器,使用非阻塞I/O,TFramedTransport必须用在这种服务器上。

三、RPC实现

①     实现服务处理接口Impl;

②     创建请求处理器TProcessor;

③     创建服务器端传输协议TServerTransport;

④     创建TProtocol;

⑤     创建TServer;

⑥     启动Server。

客户端的实现过程:

①     创建传输协议(一个Transport对象);

②     为Transport对象创建输入输出Protocol(数据交互协议);

③     基于TTransport和TProtocol创建Client的对象;

④     调用Client相应方法,发送请求,等待服务器返回结果。

 

四、实例

1、 首先根据Thrift的语法规则编写一个IDL文件Hello.thrift:

service Hello{

       stringhelloString(1:string para)

       i32helloInt(1:i32 para)

       boolhelloBoolean(1:bool para)

       voidhelloVoid()

       stringhelloNull()

}

其中定义了服务 Hello 的五个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。 Thrift 是对 IDL(Interface Definition Language) 描述性语言的一种具体实现。因此,以上的服务描述文件使用 IDL 语法编写。使用 Thrift工具编译Hello.thrift,就会生成相应的Hello.java 文件。该文件包含了在Hello.thrift 文件中描述的服务 Hello的接口定义,即Hello.Iface 接口,以及服务调用的底层通信细节,包括客户端的调用逻辑 Hello.Client 以及服务器端的处理逻辑 Hello.Processor,用于构建客户端和服务器端的功能。

2、 实现接口

创建HelloServiceImpl.java 文件并实现 Hello.java 文件中的 Hello.Iface 接口,代码如下:

import org.apache.thrift.TException;public class HelloServiceImpl implements Hello.Iface{    public boolean helloBoolean(boolean para)throws TException{       return para;    }    public int helloInt(int para)throws TException{       try {           Thread.sleep(20000);       } catch (InterruptedException e) {           // TODO Auto-generatedcatch block           e.printStackTrace();       }       return para;    }       public String helloNull()throws TException{       return null;    }       public String helloString(String para)throws TException{       return para;    }       public void helloVoid() throws TException{       System.out.println("HelloWorld!");    } }


3、 创建服务器端代码

创建服务器端实现代码,将HelloServiceImpl 作为具体的处理器传递给Thrift 服务器,代码如下:

import org.apache.thrift.TProcessor;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TBinaryProtocol.Factory;import org.apache.thrift.server.TServer;import org.apache.thrift.server.TSimpleServer;import org.apache.thrift.transport.TServerSocket;import org.apache.thrift.transport.TTransportException; public class HelloServiceServer {    /**     * 启动thrift服务器     * @paramargs     */    public static void main(String[] args) {            try{       //设置服务器端口为7911       TServerSocket serverTransport = new TServerSocket(7911);       //设置协议工厂为TBinaryProtocol.Factory       Factory proFactory = new TBinaryProtocol.Factory();       //关联处理器与Hello服务的实现       TProcessor processor = new Hello.Processor<Hello.Iface>(newHelloServiceImpl());       TServer.Args tArgs = new TServer.Args(serverTransport);       tArgs.processor(processor);       tArgs.protocolFactory(proFactory);       TServer server = new TSimpleServer(tArgs);       System.out.println("Startserver on port 7911....");       server.serve();       }catch(TTransportException e){           e.printStackTrace();       }                } }

4、 创建客户端

创建客户端实现代码,调用Hello.client 访问服务端的逻辑实现,代码如下:

import org.apache.thrift.TException;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TSocket;import org.apache.thrift.transport.TTransport;import org.apache.thrift.transport.TTransportException; public class HelloServiceClient {    /**     * 调用Hello服务     * @param args     */    public static void main(String[] args) {                   try {           //设置调用的服务器为本地,端口为7911           TTransport transport = new TSocket("localhost", 7911);           transport.open();           //设置传输协议为TBinaryProtocol           TProtocol protocol = new TBinaryProtocol(transport);           Hello.Client client = new Hello.Client(protocol);           client.helloVoid();           transport.close();                 } catch (TTransportException e) {           // TODO Auto-generatedcatch block           e.printStackTrace();       } catch (TException e) {           // TODO Auto-generatedcatch block           e.printStackTrace();       }    } }

5、 部署图


五、应用

1、 Quora

Quora是在线问答服务公司,类似于新浪微博和百度知道的合体。Thrift 用于Quara 系统后端数据的通信,服务端是用C++来实现的,客户端则是python。

2、 Evernote

EverNote 是一款非常著名的免费笔记记录软件,它最大的特点就是支持多平

台,而且数据能通过网络互相同步。Thrift 用于在多种Evernote API 平台开发的客户端与Evernote 服务器之间的通信与数据传输,Evernote API 定义了自己的Evernote Data Access and Management(EDAM) 协议规范,让客户端使用更小的网络带宽上传、下载文件和在线即时搜索服务。

3、 HBase 中的Thrift:

Thrift用于HBase中是为了提供跨平台的服务接口,在HBase 中可以使用[hbase-root]/bin/hbase thriftstart 命令启动涵盖Thrift的HBase服务端,客户端通过thrift的命令生成不同版本的客户端代码,根据定义的数据格式,对远程HBase服务端进行操作,是除了REST远程方法调用的另一种途径。

4、 其他系统:如facebook的scribe系统、淘宝的timetunnel系统和Hive等等。

 

参考文章:

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

http://dongxicheng.org/search-engine/thrift-framework-intro/

http://smallmin.iteye.com/blog/1121085


原创粉丝点击