精选实例:Thrift 实例教程(Java)
一、 About Thrift
thrift 是一种可伸缩的跨语言服务的发展软件框架。它结合了功能强大的软件堆栈的代码生成引擎,以建设服务,工作效率和无缝地与 C + +,C#,Java,Python,PHP 和 Ruby 结合。thrift 是 facebook 开发的,我们现在把它作为开源软件使用。thrift 允许你定义一个简单的定义文件中的数据类型和服务接口。以作为输入文件,编译器生成代码用来方便地生成 RPC 客户端和服务器通信的无缝跨编程语言(来自百度百科)。
最初由 facebook 开发用做系统内个语言之间的 RPC 通信 。
2007 年由 facebook 贡献到 apache 基金 ,现在是 apache 下的 opensource 之一。
支持多种语言之间的 RPC 方式的通信:php 语言 client 可以构造一个对象,调用相应的服务方法来调用 java 语言的服务 ,跨越语言的 C/S rpc 调用 。
二、 什么是 Thrift,如何工作
建立一个 java rmi 的流程:
- 定义一个服务调用接口。
- server 端:接口实现 — impl的实例 — 注册该服务实现(端口) — 启动服务。
- client 端:通过 ip、端口、服务名,得到服务,通过接口来调用。
- rmi 数据传输方式:java 对象序列化。
Thrift 服务
- 例同 rmi ,需要定义通信接口、实现、注册服务、绑定端口等。
- 多种语言之间通信。
- 数据传输走 socket(多种语言均支持),数据再以特定的格式(String ),发送,接收方语言解析。
Object ---> String ---> Object
。
存在问题:编码、解析完全需要自己做,复杂的数据结构可能会导致编码困难。
Thrift 服务 : thrift 的中间编码层
- java Object -> Thrift Object -> php Object
- 定义 thrift 的文件,由 thrift 文件(IDL)生成 双方语言的接口、model,在生成的 model 以及接口中会有解码编码的代码。
- thrift 文件生成代码命令
thrift-xx.exe -gen java TestThrift.thrift // 生成java 代码
thrift-xx.exe -gen php TestThrift.thrift // 生成php代码
thrift-xx.exe -gen py TestThrift.thrift // 生成python代码
thrift-xx.exe -gen as3 TestThrift.thrift // 生成as3代码
thrift-xx.exe -gen cpp TestThrift.thrift // 生成C++代码
三、 Thrift 安装和依赖
1)安装 thrift:到 thrift 官网下载 exe 文件,然后将文件重命名为 thrift.exe,拷贝到c:\windows目录下,或将 thrift.exe 所在路径加到 path
环境变量中,然后就可以在dos环境下使用了。如:thrift -gen java D:\mywork\javaProject\thriftTest\test.thrift ,输出的java文件默认输出到当前目录下,也可以使用 -o
参数指定输出路径。
linux 下的安装方法可以参考官方详细说明。
2)下载相关依赖包,基于 v0.9.3 测试通过
- slf4j-api-1.7.16
- slf4j-jdk14-1.7.16
- libthrift.jar,或者去 maven 仓库下载。
如果不下载 libthrift.jar 包,将 thrift 源码加入到工程下,那么还需要下载:
- httpclient-4.2.1.jar
- httpcore-4.2.1.jar
- httpmime-4.2.1.jar
- servlet-api.jar
四、 基本数据类型和集合类型
bool:布尔类型(true or value),占一个字节
byte:有符号字节
i16:16位有符号整型
i32:32位有符号整型
i64:64位有符号整型
double:64位浮点数
string:未知编码或者二进制的字符串
List<t1>:一系列t1类型的元素组成的有序表,元素可以重复
Set<t1>:一系列t1类型的元素组成的无序表,元素唯一
Map<t1,t2>:key/value对(key的类型是t1且key唯一,value类型是t2)。
五、 Thrift Demo
Thrift IDL 文件
demo.thrift
namespace java com.gemantic.analyse.thrift.index
struct NewsModel{
1:i32 id ;
2:string title;
3:string content;
4:string media_from;
5:string author;
}
service IndexNewsOperatorServices {
string indexNews(1:NewsModel indexNews),
bool deleteArtificiallyNews(1:i32 id)
}
运行 thrift --gen java demo.thrift
后生成 NewsModel.java
模型文件和 IndexNewsOperatorServices.java
服务接口文件,将文件所在包 com.gemantic.analyse.thrift.index
拷贝带 src
下。
服务接口实现
IndexNewsOperatorServicesImpl.java
package com.gemantic.analyse.thrift.index;
import org.apache.thrift.TException;
/**
* 服务实现
* @author liuqianfei
*
*/
public class IndexNewsOperatorServicesImpl implements IndexNewsOperatorServices.Iface{
@Override
public boolean deleteArtificiallyNews(int id) throws TException {
System.out.println("method deleteArtificiallyNews success !! id is :"+id);
return true;
}
@Override
public String indexNews(NewsModel indexNews) throws TException {
System.out.println("method indexNews success !! data is :");
System.out.println(indexNews);
return "客户端你好,你发送的标题为【" + indexNews.getTitle() + "】的新闻已处理,请耐心等待。";
}
}
Java 服务端
ThriftServerTest.java
package com.gemantic.analyse.thrift.index;
import java.net.InetSocketAddress;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportFactory;
/**
* 服务端
*
* @author liuqianfei
*
*/
public class ThriftServerTest
{
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args)
{
IndexNewsOperatorServices.Processor processor
= new IndexNewsOperatorServices.Processor(new IndexNewsOperatorServicesImpl());
try
{
TServerTransport serverTransport = new TServerSocket(new InetSocketAddress("127.0.0.1", 9981));
Args trArgs = new Args(serverTransport);
trArgs.processor(processor);
// 使用二进制来编码应用层的数据
trArgs.protocolFactory(new TBinaryProtocol.Factory(true, true));
// 使用普通的socket来传输数据
trArgs.transportFactory(new TTransportFactory());
TServer server = new TThreadPoolServer(trArgs);
System.out.println("server begin .......................");
server.serve();
System.out.println("---------------------------------------");
server.stop();
}
catch (Exception e)
{
throw new RuntimeException("index thrift server start failed!!" + "/n" + e.getMessage());
}
}
}
Java 客户端
ThriftClientTest.java
package com.gemantic.analyse.thrift.index;
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;
/**
* 客户端
* @author liuqianfei
*
*/
public class ThriftClientTest
{
public static void main(String[] args) throws TException
{
TTransport transport = new TSocket("127.0.0.1", 9981);
long start = System.currentTimeMillis();
TProtocol protocol = new TBinaryProtocol(transport);
IndexNewsOperatorServices.Client client = new IndexNewsOperatorServices.Client(protocol);
transport.open();
// deleteArtificiallyNews
client.deleteArtificiallyNews(123456);
// indexNews
NewsModel newsModel = new NewsModel();
newsModel.setId(789456);
newsModel.setTitle("好诗");
newsModel.setContent("长鬟如云衣似雾,锦茵罗荐承轻步。");
newsModel.setAuthor("ddc");
newsModel.setMedia_from("新华08");
String callback = client.indexNews(newsModel);
System.out.println("==>" + callback);
transport.close();
System.out.println("耗时:" + (System.currentTimeMillis() - start));
System.out.println("client sucess!");
}
}
六、 Thrift 协议栈以及各层的使用(java 为例)
- model interface : 服务的调用接口以及接口参数 model、返回值 model
- Tprotocol 协议层 : 将数据(model)编码、解码
- Ttramsport 传输层 : 编码后的数据传输(简单 socket、http)
- Tserver : 服务的 Tserver 类型,实现了几种 rpc 调用(单线程、多线程、非阻塞 IO)