【转载】Thrift概述
来自 <https://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/#ibm-pcon>
一个简单的 Thrift 实例
首先根据 Thrift 的语法规范编写脚本文件 Hello.thrift,代码如下:
清单 1. Hello.thrift
namespace java service.demo
service Hello {
string helloString(1:string para)
i32 helloInt(1:i32 para)
bool helloBoolean(1:bool para)
void helloVoid()
string helloNull()
}
其中定义了服务 Hello的五个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。 Thrift 是对 IDL(Interface Definition Language) 描述性语言的一种具体实现。因此,以上的服务描述文件使用 IDL 语法编写。使用 Thrift 工具编译 Hello.thrift,就会生成相应的 Hello.java 文件。该文件包含了在 Hello.thrift 文件中描述的服务 Hello 的接口定义,即 Hello.Iface 接口,以及服务调用的底层通信细节,包括客户端的调用逻辑 Hello.Client 以及服务器端的处理逻辑 Hello.Processor,用于构建客户端和服务器端的功能。
创建 HelloServiceImpl.java 文件并实现 Hello.java 文件中的 Hello.Iface 接口,代码如下:
清单 2. HelloServiceImpl.java
package service.demo;
import org.apache.thrift.TException;
public class HelloServiceImpl implements Hello.Iface {
@Override
public boolean helloBoolean(boolean para) throws TException {
return para;
}
@Override
public int helloInt(int para) throws TException {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return para;
}
@Override
public String helloNull() throws TException {
return null;
}
@Override
public String helloString(String para) throws TException {
return para;
}
@Override
public void helloVoid() throws TException {
System.out.println("Hello World");
}
}
创建服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给 Thrift 服务器,代码如下:
清单 3. HelloServiceServer.java
package service.server;
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.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import service.demo.Hello;
import service.demo.HelloServiceImpl;
public class HelloServiceServer {
/**
* 启动 Thrift 服务器
* @param args
*/
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(new HelloServiceImpl());
TServer server = new TThreadPoolServer(processor, serverTransport,
proFactory);
System.out.println("Start server on port 7911...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
创建客户端实现代码,调用 Hello.client 访问服务端的逻辑实现,代码如下:
清单 4. HelloServiceClient.java
package service.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;
import service.demo.Hello;
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);
// 调用服务的 helloVoid 方法
client.helloVoid();
transport.close();
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
}
}
}
代码编写完后运行服务器,再启动客户端调用服务 Hello 的方法 helloVoid,在服务器端的控制台窗口输出"Hello World"(helloVoid 方法实现在控制台打印字符串,没有返回值,所以客户端调用方法后没有返回值输出,读者可以自己尝试其他有返回值方法的调用,其结果可以打印在客户端的控制台窗口 )。
1 概述
目前流行的服务调用方式有很多种,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服务等。其中所用到的数据传输方式包括 XML,JSON 等,然而 XML 相对体积太大,传输效率低,JSON 体积较小,新颖,但还不够完善。本文将介绍由 Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言IDL(Interface Definition Language)定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。
2 Thrift 架构
Thrift 包含一个完整的堆栈结构用于构建客户端和服务器端。下图描绘了 Thrift 的整体架构。
图 1. 架构图

如图所示,图中黄色部分是用户实现的业务逻辑,褐色部分是根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架,红色部分是根据 Thrift 文件生成代码实现数据的读写操作。红色部分以下是 Thrift 的传输体系、协议以及底层 I/O 通信,使用 Thrift 可以很方便的定义一个服务并且选择不同的传输协议和传输层而不用重新生成代码。
Thrift 服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器 / 容器一起运行,可以和现有的 J2EE 服务器 /Web 容器无缝的结合。
服务端和客户端具体的调用流程如下:
图 2. Server 端启动、服务时序图(查看大图)

该图所示是 HelloServiceServer 启动的过程以及服务被客户端调用时,服务器的响应过程:
- 程序调用了 TThreadPoolServer 的 serve 方法后,server 进入阻塞监听状态,阻塞在 TServerSocket 的 accept 方法上;
- 当接收到来自客户端的消息后,服务器发起一个新线程处理这个消息请求,原线程再次进入阻塞状态
- 在新线程中,服务器通过 TBinaryProtocol 协议读取消息内容,调用 HelloServiceImpl 的 helloVoid 方法,并将结果写入 helloVoid_result 中传回客户端。
用户创建服务器的过程:
- 实现服务处理接口impl
- 创建TProcessor
- 创建TServerTransport
- 创建TProtocol
- 创建TServer
- 启动Server
图 3. Client 端调用服务时序图(查看大图)

该图所示是 HelloServiceClient 调用服务的过程以及接收到服务器端的返回值后处理结果的过程。从图中我们可以看到:
- 程序调用了 Hello.Client 的 helloVoid 方法;
- 在 helloVoid 方法中,通过 send_helloVoid 方法发送对服务的调用请求;
- 通过 recv_helloVoid 方法接收服务处理请求后返回的结果。
用户创建客户端的过程:
- 创建Transport
- 创建TProtocol
- 基于TTransport和TProtocol创建 Client
- 调用Client的相应方法
3 数据类型
Thrift 脚本可定义的数据类型包括以下几种类型:
3.1 基本类型
|
名称 |
类型 |
对应Java |
|
bool |
布尔值 |
boolean |
|
byte |
8位有符号整数 |
byte |
|
i16 |
16位有符号整数 |
short |
|
i32 |
32位有符号整数 |
int |
|
i64 |
64位有符号整数 |
long |
|
double |
64位浮点数 |
double |
|
string |
未知编码文本 或二进制字符串 |
String |
3.2 结构体类型
struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
3.3 容器类型
|
名称 |
对应Java |
|
list |
ArrayList |
|
set |
HashSet |
|
map |
HashMap |
3.4 异常类型
exception:对应 Java 的 Exception
3.5 服务类型
service:对应服务的类
4 协议
Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本 (text) 和二进制 (binary) 传输协议,为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目 / 产品中的实际需求。常用协议有以下几种:
- TBinaryProtocol —— 二进制编码格式
- TCompactProtocol —— 压缩的二进制编码格式
清单 5. 使用 TCompactProtocol 协议构建的 HelloServiceServer.java
TCompactProtocol.Factory proFactory = new TCompactProtocol.Factory();
清单 6. 使用 TCompactProtocol 协议的 HelloServiceClient.java
TCompactProtocol protocol = new TCompactProtocol(transport);
- TJSONProtocol —— 使用 JSON 的数据编码协议
清单 7. 使用 TJSONProtocol 协议构建的 HelloServiceServer.java
TJSONProtocol.Factory proFactory = new TJSONProtocol.Factory();
清单 8. 使用 TJSONProtocol 协议的 HelloServiceClient.java
TJSONProtocol protocol = new TJSONProtocol(transport);
- TSimpleJSONProtocol —— JSON 只写协议,适用于通过脚本语言解析
5 传输层
常用的传输层有以下几种:
- TSocket —— 使用阻塞式 I/O 进行传输,是最常见的模式
- TFramedTransport —— 使用非阻塞方式,按块的大小进行传输,类似于 Java 中的 NIO
若使用 TFramedTransport 传输层,其服务器必须修改为非阻塞的服务类型,客户端只需替换清单 4 中 TTransport 部分,代码如下,清单 9 中 TNonblockingServerTransport 类是构建非阻塞 socket 的抽象类,TNonblockingServerSocket 类继承 TNonblockingServerTransport
清单 9. 使用 TFramedTransport 传输层构建的 HelloServiceServer.java
TNonblockingServerTransport serverTransport;
serverTransport = new TNonblockingServerSocket(10005);
Hello.Processor processor = new Hello.Processor(new HelloServiceImpl());
TServer server = new TNonblockingServer(processor, serverTransport);
System.out.println("Start server on port 10005 ...");
server.serve();
清单 10. 使用 TFramedTransport 传输层的 HelloServiceClient.java
TTransport transport = new TFramedTransport(new TSocket("localhost", 10005)); - TNonblockingTransport —— 使用非阻塞方式,用于构建异步客户端
6 服务端类型
常见的服务端类型有以下几种:
- TSimpleServer —— 单线程服务器端使用标准的阻塞式 I/O
清单 11. 使用 TSimpleServer 服务端构建的 HelloServiceServer.java
TServerSocket serverTransport = new TServerSocket(7911);
TProcessor processor = new Hello.Processor(new HelloServiceImpl());
TServer server = new TSimpleServer(processor, serverTransport);
System.out.println("Start server on port 7911...");
server.serve(); - TThreadPoolServer —— 多线程服务器端使用标准的阻塞式 I/O
- TNonblockingServer —— 多线程服务器端使用非阻塞式 I/O
7 Thrift 异步客户端构建
Thrift 提供非阻塞的调用方式,可构建异步客户端。在这种方式中,Thrift 提供了新的类 TAsyncClientManager 用于管理客户端的请求,在一个线程上追踪请求和响应,同时通过接口 AsyncClient 传递标准的参数和 callback 对象,服务调用完成后,callback 提供了处理调用结果和异常的方法。
首先我们看 callback 的实现:
清单 12.CallBack 的实现:MethodCallback.java
package service.callback;
import org.apache.thrift.async.AsyncMethodCallback;
public class MethodCallback implements AsyncMethodCallback {
Object response = null;
public Object getResult() {
// 返回结果值
return this.response;
}
// 处理服务返回的结果值
@Override
public void onComplete(Object response) {
this.response = response;
}
// 处理调用服务过程中出现的异常
@Override
public void onError(Throwable throwable) {
}
}
如代码所示,onComplete 方法接收服务处理后的结果,此处我们将结果 response 直接赋值给 callback 的私有属性 response。
onError 方法接收服务处理过程中抛出的异常,此处未对异常进行处理。
创建非阻塞服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给异步 Thrift 服务器,代码如下:
清单 13.HelloServiceAsyncServer.java
package service.server;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TNonblockingServerTransport;
import org.apache.thrift.transport.TTransportException;
import service.demo.Hello;
import service.demo.HelloServiceImpl;
public class HelloServiceAsyncServer {
/**
* 启动 Thrift 异步服务器
* @param args
*/
public static void main(String[] args) {
TNonblockingServerTransport serverTransport;
try {
serverTransport = new TNonblockingServerSocket(10005);
Hello.Processor processor = new Hello.Processor(new HelloServiceImpl());
TServer server = new TNonblockingServer(processor, serverTransport);
System.out.println("Start server on port 10005 ...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();
}
}
}
HelloServiceAsyncServer 通过 java.nio.channels.ServerSocketChannel 创建非阻塞的服务器端等待客户端的连接。
创建异步客户端实现代码,调用 Hello.AsyncClient 访问服务端的逻辑实现,将 MethodCallback 对象作为参数传入调用方法中,代码如下:
清单 14.HelloServiceAsyncClient.java
package service.client;
import java.io.IOException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import service.callback.MethodCallback;
import service.demo.Hello;
public class HelloServiceAsyncClient {
/**
* 调用 Hello 服务
* @param args
*/
public static void main(String[] args) throws Exception {
try {
TAsyncClientManager clientManager = new TAsyncClientManager();
TNonblockingTransport transport = new TNonblockingSocket(
"localhost", 10005);
TProtocolFactory protocol = new TBinaryProtocol.Factory();
Hello.AsyncClient asyncClient = new Hello.AsyncClient(protocol,
clientManager, transport);
System.out.println("Client calls .....");
MethodCallback callBack = new MethodCallback();
asyncClient.helloString("Hello World", callBack);
Object res = callBack.getResult();
while (res == null) {
res = callBack.getResult();
}
System.out.println(((Hello.AsyncClient.helloString_call) res)
.getResult());
} catch (IOException e) {
e.printStackTrace();
}
}
}
HelloServiceAsyncClient 通过 java.nio.channels.Socketchannel 创建异步客户端与服务器建立连接。在本文中异步客户端通过以下的循环代码实现了同步效果,读者可去除这部分代码后再运行对比。
清单 15. 异步客户端实现同步效果代码段
Object res = callBack.getResult();
// 等待服务调用后的返回结果
while (res == null) {
res = callBack.getResult();
}
通过与清单 9 和清单 10 的代码比较,我们可以构建一个 TNonblockingServer 服务类型的服务端,在客户端构建一个 TFramedTransport 传输层的同步客户端和一个 TNonblockingTransport 传输层的异步客户端,那么一个服务就可以通过一个 socket 端口提供两种不同的调用方式。
【转载】Thrift概述的更多相关文章
- [转载] Thrift原理简析(JAVA)
转载自http://shift-alt-ctrl.iteye.com/blog/1987416 Apache Thrift是一个跨语言的服务框架,本质上为RPC,同时具有序列化.发序列化机制:当我们开 ...
- 【通信框架】Apache的开源通信框架thrift概述
在阅读的过程中有不论什么问题.欢迎一起交流 邮箱:1494713801@qq.com QQ:1494713801 一.作用 Thrift("Scalable Cross-Languag ...
- thrift概述
Apache Thrift 是FaceBook实现的一种跨平台的远程服务调用(RPC)的框架.它采用接口描述语言(IDL)定义并创建服务,传输数据采用二进制格式,相对于XML和Json等常用数据传输方 ...
- <转载>css3 概述
参照 https://www.ibm.com/developerworks/cn/web/1202_zhouxiang_css3/ http://www.cnblogs.com/ghost-xyx/p ...
- Thrift 跨服务开发框架
Thrift概述 Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP,Ruby, Erla ...
- [ToDo]Thrift学习
这里有较详细的Java项目配置过程: http://bglmmz.iteye.com/blog/2058785 下面有Java项目的示例介绍: http://www.tuicool.com/artic ...
- IIC协议理解(转)
目录 IIC协议理解(转) 个人小结记录 (记一下这个就够了) 以下为转载记录 概述 概述 输出级 主设备与从设备 速率 时序 空闲状态 起始位与停止位 数据的有效性 数据的传送 工作过程 主设备向从 ...
- Java内存管理 -JVM 垃圾回收
版权声明:本文为博主原创文章,未经博主允许不得转载 一.概述 相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存. 在之前的JVM 之内存管理 中,我们介绍 ...
- Sql Full-Text Filter
SQL Server 全文索引介绍(转载) 概述 全文引擎使用全文索引中的信息来编译可快速搜索表中的特定词或词组的全文查询.全文索引将有关重要的词及其位置的信息存储在数据库表的一列或多列中.全文索引是 ...
随机推荐
- hive单节点数据倾斜解决方法
一.现象 map/reduce程序执行时,reduce节点大部分执行完毕,但是有一个或者几个reduce节点运行很慢,导致整个程序的处理时间很长,这是因为某一个key的条数比其他key多很多(有时是百 ...
- RTX二次开发::检索 COM 类工厂中 CLSID 为 {79210E58-99EB-45D0-8890-763EFEAA143F} 的组件失败,
解决方法: 1.Interop.RTXSAPILib.dll这个是32位的 将IIS 应用程序池 启用32位应用程序 设置为true就可以了, 2.把编译好的程序选择X86平台运行,就好了 程序员的基 ...
- SQL Server 2008 收缩日志
如果SQL SERVER 日志过大,比如,达到了几十个G,想一次性收缩的,直接执行下面命令即可: USE DATABASENAME; GO – Truncate the log by changing ...
- crontab定期执行shell脚本
[场景]: 需要定期运行Hadoop的MapReduce [解决办法]: 编写一个运行MapReduce的Shell脚本.然后使用crontab配置定时任务. [Shell脚本] #!/usr/bin ...
- spring加载ApplicationContext.xml的四种方式
spring 中加载xml配置文件的方式,好像有4种, xml是最常见的spring 应用系统配置源.Spring中的几种容器都支持使用xml装配bean,包括: XmlBeanFactory , C ...
- Oracle——视图
视图是一种虚表. 视图建立在已有表的基础上, 视图依赖的这些表称为基表. 视图向用户提供基表数据的另一种表现形式 对视图数据的修改会影响到基表中的数据 视图的优点 控制数据访问 简化查询 避免重复访问 ...
- (转)Linux下安装Android的adb驱动-解决不能识别的问题(国产板子)
注:本文不涉及怎么安装adb等工具.本方法通用.到目前为止已经解决rk3188和展讯SC6820(波导T9500)的识别问题. 以前调试的Android板子,都是直接用的Google的usb设备id, ...
- Java基本类型学习
基本类型,或者叫做内置类型,是JAVA中不同于类的特殊类型.它们是我们编程中使用最频繁的类型.java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为变量的初始化. 1. Java ...
- setoolkit基础
1.社会工程学攻击 2.快速追踪测试 3.第三方模块 4.升级软件 5.升级配置 6.帮助 99.退出 1.鱼叉式网络钓鱼攻击 2.网页攻击 3.传染媒介式(俗称木马) 4.建立payloaad和li ...
- jquery插件中(function ( $, window, document, undefined )的作用
在jquery插件中我们经常看到以下这段代码 ;(function ( $, window, document, undefined ){ //函数体内具体代码 })(jQuery, window,d ...