Thrift 入门
2016-06-14 20:42
295 查看
1.Thrift 是什么
Apache Thrift 是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架2. 为什么是Thrift ,相比rest, soap等远程调用方式
目前流行的服务调用方式有很多种,例如基于SOAP 消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服务等。其中所用到的数据传输方式包括 XML,JSON 等,然而 XML 相对体积太大,传输效率低,JSON 体积较小,新颖,但还不够完善。本文将介绍由 Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell,
C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。本文将详细介绍 Thrift 的使用,并且提供丰富的实例代码加以解释说明,帮助使用者快速构建服务。
3. 怎么用
To install plugin in Eclipse 3.4:Open Help > Software Updates
Select Add Site...
Enter http://thrift4eclipse.sourceforge.net/updatesite/
Select Thrift4Eclipse
Click Install
<!-- http://mvnrepository.com/artifact/org.apache.thrift/libthrift -->
<dependency>
<groupId>org.apache.thrift</groupId>
<artifactId>libthrift</artifactId>
<version>0.9.3</version>
<type>pom</type>
</dependency>
从thrif 的 idl文件生成javacode,thrift文件定义如下:
namespace java com.thrift service Hello{ string helloString(1:string para) i32 helloInt(1:i32 para) bool helloBoolean(1:bool para) void helloVoid() string helloNull() }
下载工具从thrift的idl文件生成java code http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.exe
下面是从thrift文件生成的部分code,删除了大部分自动生成的code,主要保留了定义的接口:
package com.thrift; import org.apache.thrift.scheme.IScheme; import org.apache.thrift.scheme.SchemeFactory; import org.apache.thrift.scheme.StandardScheme; import org.apache.thrift.scheme.TupleScheme; import org.apache.thrift.protocol.TTupleProtocol; import org.apache.thrift.protocol.TProtocolException; import org.apache.thrift.EncodingUtils; import org.apache.thrift.TException; import org.apache.thrift.async.AsyncMethodCallback; import org.apache.thrift.server.AbstractNonblockingServer.*; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.EnumMap; import java.util.Set; import java.util.HashSet; import java.util.EnumSet; import java.util.Collections; import java.util.BitSet; import java.nio.ByteBuffer; import java.util.Arrays; import javax.annotation.Generated; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"}) @Generated(value = "Autogenerated by Thrift Compiler (0.9.3)", date = "2016-06-14") public class Hello { public interface Iface { public String helloString(String para) throws org.apache.thrift.TException; public int helloInt(int para) throws org.apache.thrift.TException; public boolean helloBoolean(boolean para) throws org.apache.thrift.TException; public void helloVoid() throws org.apache.thrift.TException; public String helloNull() throws org.apache.thrift.TException; }
服务实现类:
package com.thrift; import org.apache.thrift.TException; public class HelloServiceImpl implements Hello.Iface { public String helloString(String para) throws TException { // TODO Auto-generated method stub return "hello thrift"; } public int helloInt(int para) throws TException { // TODO Auto-generated method stub return 0; } public boolean helloBoolean(boolean para) throws TException { // TODO Auto-generated method stub return false; } public void helloVoid() throws TException { // TODO Auto-generated method stub } public String helloNull() throws TException { // TODO Auto-generated method stub return null; } }
服务端启动代码
package com.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 服务器 * @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<Hello.Iface>(new HelloServiceImpl()); TServer.Args tArgs = new TServer.Args(serverTransport); tArgs.processor(processor); tArgs.protocolFactory(proFactory); //使用TSimpleServer TServer server = new TSimpleServer(tArgs); System.out.println("Start server on port 7911...."); server.serve(); }catch(TTransportException e){ e.printStackTrace(); } } }客户端调用代码:
package com.thrift; 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); System.out.println(client.helloString("")); transport.close(); } catch (TTransportException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (TException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4. 数据类型
Thrift 脚本可定义的数据类型包括以下几种类型:基本类型:
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:未知编码文本或二进制字符串,对应 Java 的 String
结构体类型:
struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
容器类型:
list:对应 Java 的 ArrayList
set:对应 Java 的 HashSet
map:对应 Java 的 HashMap
异常类型:
exception:对应 Java 的 Exception
服务类型:
service:对应服务的类
Thrift 可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本 (text) 和二进制 (binary) 传输协议,为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目 / 产品中的实际需求。常用协议有以下几种:
TBinaryProtocol —— 二进制编码格式进行数据传输
使用方法如清单 3 和清单 4 所示。
TCompactProtocol —— 高效率的、密集的二进制编码格式进行数据传输
构建 TCompactProtocol 协议的服务器和客户端只需替换清单 3 和清单 4 中 TBinaryProtocol 协议部分即可,替换成如下代码:
清单 5. 使用 TCompactProtocol 协议构建的 HelloServiceServer.java
TCompactProtocol.Factory proFactory = new TCompactProtocol.Factory();
清单 6. 使用 TCompactProtocol 协议的 HelloServiceClient.java
TCompactProtocol protocol = new TCompactProtocol(transport);
TJSONProtocol —— 使用 JSON 的数据编码协议进行数据传输
构建 TJSONProtocol 协议的服务器和客户端只需替换清单 3 和清单 4 中 TBinaryProtocol 协议部分即可,替换成如下代码:
清单 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 进行传输,是最常见的模式
使用方法如清单 4 所示。
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 —— 使用非阻塞方式,用于构建异步客户端
使用方法请参考 Thrift 异步客户端构建
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();
客户端的构建方式可参考清单 4。
TThreadPoolServer —— 多线程服务器端使用标准的阻塞式 I/O
使用方法如清单 3 所示。
TNonblockingServer —— 多线程服务器端使用非阻塞式 I/O
使用方法请参考 Thrift 异步客户端构建
7. Thrift 异步客户端构建
Thrift 提供非阻塞的调用方式,可构建异步客户端。在这种方式中,Thrift 提供了新的类 TAsyncClientManager 用于管理客户端的请求,在一个线程上追踪请求和响应,同时通过接口 AsyncClient 传递标准的参数和 callback 对象,服务调用完成后,callback 提供了处理调用结果和异常的方法。首先我们看 callback 的实现
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 服务器,代码如下:
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 对象作为参数传入调用方法中,代码如下:
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 创建异步客户端与服务器建立连接。在本文中异步客户端通过以下的循环代码实现了同步效果,读者可去除这部分代码后再运行对比
Object res = callBack.getResult(); // 等待服务调用后的返回结果 while (res == null) { res = callBack.getResult(); }
通过与清单 9 和清单 10 的代码比较,我们可以构建一个 TNonblockingServer 服务类型的服务端,在客户端构建一个 TFramedTransport 传输层的同步客户端和一个 TNonblockingTransport 传输层的异步客户端,那么一个服务就可以通过一个 socket
端口提供两种不同的调用方式。有兴趣的读者可以尝试一下
8.适用场景及优缺点
http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/相关文章推荐
- MYSQL企业常用架构与调优经验分享
- 上周作业
- JSON
- 海康ipc onvif抓包分析
- LCD(一) TFT液晶时序图
- Python里面的字典
- How to install Windows device driver, Vista, Vista x64, WinXP, WinXP x64 Window
- 与其他APP进行交互
- Qt Dll总结——链接库预备知识(转载)
- Android中的多线程
- MySQL配置文件my.cnf 详解
- Android Canvas绘图详解(图文)
- java反射
- ArrayList、Vector和LinkedList等的差别与用法(基础回顾)
- 全志平台TP驱动
- Mendeley搭配WORD寫論文
- Android Studio中Gradle使用详解
- (OK) netcat transfer file to android from fedora23
- Thinkphp 关联关系一对多,取出数组
- 输出坐标值