Thrift源于大名鼎鼎的facebook之手,在2007年facebook提交Apache基金会将Thrift作为一个开源项目,对于当时的facebook来说创造thrift是为了解决facebook系统中各系统间大数据量的传 输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于程序对程 序静态的数据交换,需要先确定好他的数据结构,他是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。
Thrift如何使用
本文基于thrift最新版本0.6.1。
首先呢,Thrift使用了ThriftIDL来定义服务器和客户端的接口。例如,这是本文使用的一个thrift(calculator.thrift)文件。
namespace java calculator
/*
Thrift的注释与C++或Java类似
*/
/*
在这里我们定义了加减乘除的一个枚举类型
*/
enum Operation {
ADD,
SUBTRACT,
MULTIPLY,
DIVIDE
}
/*
Thrift支持自定义的异常
*/
exception InvalidOperation {
1: i32 errorCode,
2: string message
}
/*
定义一下我们的Service接口
*/
service Calculator {
i32 calculate(1:Operation operation, 2:i32 num1, 3:i32 num2) throws (1:InvalidOperation e),
}
接下来,我们需要把这个thrift文件编译成Java文件。
我们需要的编译工作可以到http://thrift.apache.org/download/下载:Thrift compiler for Windows (thrift-0.6.1.exe) 。千万不要下载“Snapshot Releases”的,这个不一定能用,我就下错了,结果无法使用,呵呵。
接下来我们运行命令
thrift-0.6.1.exe -o src --gen java calculator.thrift
可以看到在src目录下生成了如下的文件
gen-java\calculator\Calculator.java
gen-java\calculator\InvalidOperation.java
gen-java\calculator\Operation.java
把这几个文件加入到你的项目中吧。
下面,我们需要thrift的Java库。
使用maven或ivy的可以参考http://mvnrepository.com/artifact/org.apache.thrift/libthrift/0.6.1进行下载
thrift没有提供完整的下载,不过既然是搞Java的,起码要会maven或ivy吧。
实在不行,就去http://svn.apache.org/repos/asf/thrift/trunk/lib/java/下载源代码,然后运行一下ant就ok啦。
到这里我们已经准备好了thrift。
我们看一下thrift的架构
![](http://upload.wikimedia.org/wikipedia/en/thumb/d/df/Apache_Thrift_architecture.png/400px-Apache_Thrift_architecture.png)
thrift最大的功劳就在于他帮助我们实现了传输层(TTransport)和协议层(TProtocol),我们只需要选择需要一个适合自己的实现就ok啦。
例如,我们客户端可以这么写
03 |
import org.apache.thrift.TException; |
04 |
import org.apache.thrift.protocol.TBinaryProtocol; |
05 |
import org.apache.thrift.protocol.TProtocol; |
06 |
import org.apache.thrift.transport.TSocket; |
07 |
import org.apache.thrift.transport.TTransport; |
08 |
import org.apache.thrift.transport.TTransportException; |
12 |
public static void main(String[] args) { |
19 |
TTransport transport = new TSocket( "localhost" , 9090 ); |
27 |
TProtocol protocol = new TBinaryProtocol(transport); |
29 |
Calculator.Client client = new Calculator.Client(protocol); |
34 |
System.out.println(client.calculate(Operation.ADD, 1 , 2 )); |
35 |
System.out.println(client.calculate(Operation.SUBTRACT, 3 , 5 )); |
36 |
System.out.println(client.calculate(Operation.MULTIPLY, 4 , 2 )); |
37 |
System.out.println(client.calculate(Operation.DIVIDE, 1 , 0 )); |
39 |
} catch (TTransportException e) { |
41 |
} catch (InvalidOperation e) { |
43 |
} catch (TException e) { |
这个客户端很简单,设置一个传输层(TTransport)和协议层(TProtocol)的实现类TSocket和TBinaryProtocol,就可以使用thrift帮我们生成的类alculator.Client(这个完全不用我们写一行代码的,自动生成的类)来调用服务器上的真正的Calculator对象了,用起来跟直接调用函数差不多,把网络传输细节都隐藏起来了。
下面我们看一下服务器的实现。
03 |
import org.apache.thrift.TException; |
04 |
import org.apache.thrift.server.TServer; |
05 |
import org.apache.thrift.server.TSimpleServer; |
06 |
import org.apache.thrift.transport.TServerSocket; |
07 |
import org.apache.thrift.transport.TServerTransport; |
08 |
import org.apache.thrift.transport.TTransportException; |
15 |
public static class CalculatorHandler implements Calculator.Iface { |
18 |
public int calculate(Operation operation, int num1, int num2) |
19 |
throws InvalidOperation, TException { |
29 |
throw new InvalidOperation( 1 , "divide by zero" ); |
33 |
throw new InvalidOperation( 0 , "impossible code" ); |
39 |
public static void main(String[] args) { |
42 |
Calculator.Processor processor = new Calculator.Processor( |
43 |
new CalculatorHandler()); |
46 |
TServerTransport serverTransport = new TServerSocket( 9090 ); |
52 |
TServer server = new TSimpleServer( |
53 |
new TServer.Args(serverTransport).processor(processor)); |
60 |
} catch (TTransportException e) { |
服务器的实现也一样,设置一个传输层(TTransport)和协议层(TProtocol)的实现类TSocket和TBinaryProtocol,然后直接调用TServer的serve函数就ok了。之后服务器就挂起等待客户端的请求了。
当然,我们还需要在服务器这边写一个真正的CalculatorHandler实例。这个实例在服务器这边进行真正的计算,然后有TServer自动地把结果传递给client。
Thrift比起protobuf的优点
protobuf也提供了跨语言的数据传输解决方案,但是没有thrift的server和client的功能。使用protobuf实现server和client需要自己编写网络传输的代码(缺少thrift的TTransport和TProtocol这两层的实现)。另外,protobuf只支持c++,python,java,语言数量比较少。Thrift支持C++, C#, Erlang, Haskell, Java, Objective C/Cocoa, OCaml, Perl, PHP, Python, Ruby, and Squeak等各种听过或没听过的语言^_^。当然protobuf优势在于传输的数据比thrift小一些,当然差距不是很大。另外,事实上protobuf完全可以实现server/client功能,从protobuf的service功能可以看出来,不知道为什么google没有实现,而是留给用户去实现。
参考资源
http://www.javabloger.com/article/apache-thrift-architecture.html
http://en.wikipedia.org/wiki/Apache_Thrift
http://thrift.apache.org/
http://wiki.apache.org/thrift/