您的位置:首页 > 其它

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/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: