了解Thrift全貌

来源:互联网 发布:网络运营主管 编辑:程序博客网 时间:2024/05/10 15:18
第1章介绍

1.1前言

       Apache Thrift是Facebook实现的一种高效的、跨语言的服务部署框架。最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(目前支持As3,C Glib,C++,CSharp,D,Delphi,Erlang,Go,Haskell,Java,Java Me,JavascriptNode.jsObjective-cOCaml, Perl,PHP,Python,Ruby,Smalltalk),并由生成的代码负责RPC协议层和传输层的实现。

1.2架构


       Thrift实际上是实现了C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提供服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。

1.2.1传输方式(Transport)

       Transport层提供了一个简单的网络读写抽象层。这使得Thrift底层的transport从系统其它部分(如序列化/反序列化)解耦。以下是一些Transport接口提供的方法:

  • open
  • close
  • read
  • write
  • flush

       除了以上几个接口,Thrift使用ServerTransport接口接受或者创建原始transport对象。正如名字暗示的那样,ServerTransport用在server端,为到来的连接创建Transport对象。ServerTransport提供的方法有:

  • listen
  • accept
  • close

       常用的传输方式有以下几种:

  • TSocket——阻塞式socket。
  • TFramedTransport——使用非阻塞方式,按块的大小进行传输,类似于 Java中的NIO若使用TFramedTransport传输层,其服务器必须为非阻塞的服务类型。
  •  TFileTransport——以文件形式进行传输。
  • TMemoryInputTransport——将内存用于I/O。Java实现时内部实际使用了简单的ByteArrayOutputStream。
  • TNonblockingTransport——使用非阻塞方式,用于构建异步客户端。

1.2.2协议(Protocol)

       Protocol抽象层定义了一种将内存中数据结构映射成可传输格式的机制。换句话说,Protocol定义了datatype怎样使用底层的Transport对自己进行编解码。因此,Protocol的实现要给出编码机制并负责对数据进行序列化。

       Thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本 (text) 和二进制 (binary) 传输协议,为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:

  • TBinaryProtocol ——二进制编码格式进行数据传输。
  • TCompactProtocol——高效率的、压缩的二进制编码格式进行数据传输。
  • TJSONProtocol——使用JSON的数据编码协议进行数据传输。
  • TSimpleJSONProtocol——提供JSON只写协议, 适用于通过脚本语言解析。

1.2.3处理器(Processor)

       Processor封装了从输入数据流中读数据和向数据数据流中写数据的操作。读写数据流用Protocol对象表示。Processor的结构体非常简单:

interface TProcessor {          bool process(TProtocol in, TProtocol out) throws TException}
       与服务相关的Processor实现由编译器产生。Processor主要工作流程如下:从连接中读取数据(使用输入protocol),将处理授权给handler(由用户实现),最后将结果写到连接上(使用输出protocol)。

1.2.4服务类型

       Server将以上所有特性集成在一起:

       (1)创建一个transport对象。

       (2)为transport对象创建输入输出protocol。

       (3)基于输入输出protocol创建processor。

       (4)等待连接请求并将之交给processor处理。

       常用的Server有:

  • TSimpleServer——简单的单线程服务模型,常用于测试。
  • TThreadPoolServer——多线程服务模型,使用标准的阻塞式IO。
  • TNonblockingServer——多线程服务模型,使用非阻塞式IO。

 

第2章Thrift类型

2.1基本类型

  • bool:布尔值,true或false,对应Java的boolean。
  • byte:8位有符号整数,对应Java的byte。
  • i16:16位有符号整数,对应Java的short。
  • i32:32位有符号整数,对应Java的int。
  • i64:64位有符号整数,对应Java的long。
  • double:64位浮点数,对应Java的double。
  • string:utf-8编码的字符串,对应Java的String。

2.2特殊类型

  • binary:未编码的字节序列,对应Java的ByteBuffer。

2.3结构体类型

  • struct:定义公共的对象,类似于C语言中的结构体定义,在Java中是一个 JavaBean。

2.4容器类型

  • list:对应Java的ArrayList。
  • set:对应Java的HashSet。
  • map:对应Java的HashMap。

2.5异常类型

  • exception:对应Java的Exception。

2.6服务类型

  • service:对应服务的类。

第3章Thrift实例

3.1生成Thrift代码

3.1.1编写Thrift文件

       创建一个简单的服务Hello。首先根据Thrift的语法规范编写脚本文件 Hello.thrift,代码如下:

    namespace java com.joyveb.demo

     serviceHello{

               string helloString(1:string para)

               i32 helloInt(1:i32 para)

     }

       其中定义了Hello服务的2个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。Thrift是对IDL(Interface Definition Language) 描述性语言的一种具体实现。因此,以上的服务描述文件使用 IDL 语法编写。

3.1.2代码生成

  • 在Thrift官网下载Thrift编译器(windows下现为thrift-0.9.2.exe)。
  • 将Thrift文件和Thrift编译器放到同一目录(方便编译),在cmd命令提示符中进入该目录,执行:

             thrift-0.9.0 --gen  java  Hello.thrift

       编译完成后,会在同目录下的gen-java文件夹下生成相应的Hello.java文件。该文件包含了在 Hello.thrift 文件中描述的服务 Hello 的接口定义,即Hello.Iface接口,以及服务调用的底层通信细节,包括客户端的调用逻辑Hello.Client以及服务器端的处理逻辑 Hello.Processor,用于构建客户端和服务器端的功能。



3.2服务端

       将生成的代码copy到自己的工程下,如果工程是maven构建的,在pom.xml文件中添加:

               <dependency>

                         <groupId>org.apache.thrift</groupId>

                         <artifactId>libthrift</artifactId>

                         <version>0.9.0</version>

               </dependency>

  • 实现服务处理接口:创建HelloImpl.java文件并实现Hello.java文件中的Hello.Iface接口,代码如下:

  

  • 创建TProcessor。
  • 创建TServerTransport。
  • 创建TProtocol。
  • 创建TServer。
  • 启动Server。

服务端代码如下:

public class ServerSimple {public static final int SERVER_PORT = 2013;public void startServer() {try {System.out.println("HelloWorld TSimpleServer start ....");TProcessor tprocessor = new Hello.Processor<Hello.Iface>(new HelloImpl());// 简单的单线程服务模型,一般用于测试TServerSocket serverTransport = new TServerSocket(SERVER_PORT);TServer.Args tArgs = new TServer.Args(serverTransport);tArgs.processor(tprocessor);tArgs.protocolFactory(new TBinaryProtocol.Factory());// tArgs.protocolFactory(new TCompactProtocol.Factory());// tArgs.protocolFactory(new TJSONProtocol.Factory());TServer server = new TSimpleServer(tArgs);server.serve();} catch (Exception e) {System.out.println("Server start error!!!");e.printStackTrace();}}public static void main(String[] args) {ServerSimple server = new ServerSimple();server.startServer();}}
     服务端序列图如下:


3.3客户端

  • 创建Transport。
  • 创建TProtocol。
  • 基于TTransport和TProtocol创建Client。
  • 调用Client的相应方法。

     对应客户端代码如下:

public class ClientSimple {public static final String SERVER_IP = "localhost";public static final int SERVER_PORT = 2013;public static final int TIMEOUT = 30000;public void startClient(String userName) {TTransport transport = null;try {transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);// 协议要和服务端一致TProtocol protocol = new TBinaryProtocol(transport);Hello.Client client = new Hello.Client(protocol);transport.open();String result = client.helloString(userName);System.out.println("Thrify client result =: " + result);} catch (TTransportException e) {e.printStackTrace();} catch (TException e) {e.printStackTrace();} finally {if (null != transport) {transport.close();}}}public static void main(String[] args) {ClientSimple client = new ClientSimple();client.startClient("Michael");}} 

      客户端序列图如下:


     注意:客户端和服务端的协议要一致。

第4章参考资料

  • Apache Thrift 官网Thrift源码和使用说明等
  • Thrift入门及Java实例演示:Thrift入门很好的参考
  • Thrift框架介绍
  • Thrift使用指南
  • 使用ThriftRPC编写程序
  • 浅谈Thrift内部实现原理
  • ApacheThrift - 可伸缩的跨语言服务开发框架