昨天写的 thrift 基础的例子
1、thrift 是什么:
thrift 是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。thrift 最初由 facebook 开发,07年四月开放源码,08年5月进入apache孵化器。(这段话是百度的)
现在的官方网站:http://thrift.apache.org/ 最新版0.9.2
2、开发简单的例子
实际上thrift就是生成一个各个语言下都能通用的Entity类,Service接口和通讯协议。
首先要到官网上下载thrift的可执行程序,用于生成上边写的Entity和Service接口。
下载地址:http://thrift.apache.org/download
下载 Thrift compiler 即可,我是 windows 直接改名称为 thrift.exe 放到了系统的 windows 目录下。所以到时候命令行直接执行命令即可(懒得配单独的环境变量啊)。
开发:
使用的是Java
先是maven的配置
<dependency> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId> <version>0.9.2</version> </dependency>
PS:话说才发现 storm 里也有 thrift 的包不过包名怎么都改成了 org.apache.thrift7 了。
首先是Entity类和Service接口:
随便找个文本文件管理器比如Notepad++或者SublimeText之类的写一个文件,文件名helloworld.thrift
内容:
namespace java com.nanxiaoqiang.test.thrift.demo1// java下的包路径 // 生成方式 bash:thrift -r -gen java helloworld.thrift struct Helloworld{ 1:i32 id; 2:string name; } service HelloworldService{ bool insertHelloworld(1:Helloworld helloworld), bool removeHelloworld(1:i32 id), Helloworld getHelloworld(1:i32 id) }
然后命令行下 thrift -r -gen java helloworld.thrift 即可生成两个文件:Helloworld.java和HelloworldService.java。
PS:关于这个IDL类型的文件到底应当怎么写,下边的是参考文档:
http://thrift.apache.org/docs/types 官方文档
http://wiki.apache.org/thrift/ Wiki百科
http://www.cnblogs.com/tianhuilove/archive/2011/09/05/2167669.html tianhuilove的博客
接着是具体Service的实现,模拟了一个类似于CURD的方法内容
package com.nanxiaoqiang.test.thrift.demo1;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TException;
import com.nanxiaoqiang.test.thrift.demo1.HelloworldService.Iface;
/**
* Handler 数据处理接口
*
* @author nanxiaoqiang
*
* @version 0.1
*
* @since 2015年3月21日
*
*/
public class HelloworldServiceImpl implements Iface {
private static Logger logger = LogManager
.getLogger(HelloworldServiceImpl.class.getName());
ConcurrentHashMap<Integer, Helloworld> map = new ConcurrentHashMap<>();
public HelloworldServiceImpl() {
Helloworld h = new Helloworld();
h.setId(0);
h.setName("NULL");
map.put(h.getId(), h);
logger.info("constructor HelloworldServiceImpl:add new Helloworld,now map size is "
+ map.size());
}
@Override
public boolean insertHelloworld(Helloworld helloworld) throws TException {
if (helloworld == null || helloworld.getId() == 0) {
logger.info("error object of Helloworld. map size is " + map.size());
return false;
} else {
logger.info(ToStringBuilder.reflectionToString(helloworld,
ToStringStyle.MULTI_LINE_STYLE));
map.put(helloworld.getId(), helloworld);
logger.info("insert complete. map size is " + map.size());
return true;
}
}
@Override
public boolean removeHelloworld(int id) throws TException {
if (id == 0) {
logger.info("can not remove with id 0. map size is " + map.size());
return false;
} else if (map.containsKey(id)) {
logger.info(ToStringBuilder.reflectionToString(map.get(id),
ToStringStyle.MULTI_LINE_STYLE));
map.remove(id);
logger.info("removed complete with id " + id + ". map size is "
+ map.size());
return true;
} else {
logger.info("can not find object with id " + id + ". map size is "
+ map.size());
return false;
}
}
@Override
public Helloworld getHelloworld(int id) throws TException {
if (map.containsKey(id)) {
Helloworld h = map.get(id);
logger.info(ToStringBuilder.reflectionToString(h,
ToStringStyle.MULTI_LINE_STYLE));
return h;
} else {
logger.info("can not find object with id " + id + ". map size is "
+ map.size());
return null;
}
}
}
接着是 Server 端:
package com.nanxiaoqiang.test.thrift.demo1;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
/**
* 一个基本的Thrift的例子,Server 服务模型,线程安全
*
* @author nanxiaoqiang
*
* @version 0.1
*
* @since 2015年3月21日
*
*/
public class Server {
private static Logger logger = LogManager.getLogger(Server.class.getName());
public static final int SERVER_PORT = 9999;
public Server() {
}
public void startServer() {
logger.info("准备启动TProcessor");
TProcessor tprocessor = new HelloworldService.Processor<HelloworldService.Iface>(
new HelloworldServiceImpl());
// 简单的单线程服务模型,一般用于测试
try {
// 支持的服务模型
// TSimpleServer – 简单的单线程服务模型,常用于测试
// TThreadedServer - 多线程服务模型,使用阻塞式IO,每个请求创建一个线程。
// TThreadPoolServer – 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
// TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)
// 处理大量更新的话,主要是在TThreadedServer和TNonblockingServer中进行选择。
// TNonblockingServer能够使用少量线程处理大量并发连接,但是延迟较高;
// TThreadedServer的延迟较低。
// 实际中,TThreadedServer的吞吐量可能会比TNonblockingServer高,
// 但是TThreadedServer的CPU占用要比TNonblockingServer高很多。
TServerSocket serverTransport = new TServerSocket(SERVER_PORT);
TServer.Args tArgs = new TServer.Args(serverTransport);
tArgs.processor(tprocessor);
// 支持的传输格式
// TBinaryProtocol – 二进制格式.
// TCompactProtocol – 压缩格式
// TJSONProtocol – JSON格式
// TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
// TDebugProtocol – 使用易懂的可读的文本格式,以便于debug
tArgs.protocolFactory(new TBinaryProtocol.Factory());// 传输格式,二进制
// tArgs.protocolFactory(new TCompactProtocol.Factory()); // 传输格式,压缩
// tArgs.protocolFactory(new TJSONProtocol.Factory());// 传输格式,JSON
TServer server = new TSimpleServer(tArgs);
server.serve();
logger.info("启动完成.");
} catch (TTransportException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}
public static void main(String[] args) {
Server server = new Server();
server.startServer();
}
}
Client 端:
package com.nanxiaoqiang.test.thrift.demo1;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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 Client {
private static Logger logger = LogManager.getLogger(Client.class.getName());
public static final String SERVER_IP = "localhost";
public static final int SERVER_PORT = 9999;
public static final int TIMEOUT = 30000;// 超时
public Client() {
}
public void startclient() {
// 支持的通信方式(数据传输方式)(Transport)
// TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。
// THttpTransport:采用Http传输协议进行数据传输
// TSocket:采用TCP Socket进行数据传输
// TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压
// 下面几个类主要是对上面几个类地装饰(采用了装饰模式),以提高传输效率。
// TBufferedTransport:对某个Transport对象操作的数据进行buffer,即从buffer中读取数据进行传输,或者将数据直接写入buffer
// TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用。同TBufferedTransport类似,也会对相关数据进行buffer,同时,它支持定长数据发送和接收。
// TMemoryBuffer:从一个缓冲区中读写数据
TTransport transport = new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT);
// 协议要和服务端一致
TProtocol protocol = new TBinaryProtocol(transport);
// TProtocol protocol = new TCompactProtocol(transport);
// TProtocol protocol = new TJSONProtocol(transport);
HelloworldService.Client client = new HelloworldService.Client(protocol);
try {
transport.open();
// 测试1
Helloworld h1 = null;
boolean b1 = client.insertHelloworld(h1);
logger.info("传输一个null值进行insertHelloworld方法,返回结果:" + b1);
TimeUnit.SECONDS.sleep(1);
// 测试2
Helloworld h2 = new Helloworld();
h2.setId(1);
h2.setName("Hello World!Thrift!");
boolean b2 = client.insertHelloworld(h2);
logger.info("传输一个Helloworld对象进行insertHelloworld方法,返回结果:" + b2);
TimeUnit.SECONDS.sleep(1);
// 测试3
Helloworld h3 = client.getHelloworld(0);
logger.info("通过方法getHelloworld得到id为0的Helloworld对象,返回结果:"
+ ToStringBuilder.reflectionToString(h3,
ToStringStyle.MULTI_LINE_STYLE));
TimeUnit.SECONDS.sleep(1);
// 测试4
Helloworld h4 = client.getHelloworld(1);
logger.info("通过方法getHelloworld得到id为1的Helloworld对象,返回结果:"
+ ToStringBuilder.reflectionToString(h4,
ToStringStyle.MULTI_LINE_STYLE));
TimeUnit.SECONDS.sleep(1);
// 测试5
// 此段会报错!不能返回null对象
// 报错内容:org.apache.thrift.TApplicationException: getHelloworld failed: unknown result
// Helloworld h5 = client.getHelloworld(2);
// logger.info("通过方法getHelloworld得到id为2的Helloworld对象,返回结果:"
// + ToStringBuilder.reflectionToString(h5,
// ToStringStyle.MULTI_LINE_STYLE));
// TimeUnit.SECONDS.sleep(1);
// 测试6
boolean b6 = client.removeHelloworld(0);
logger.info("通过方法removeHelloworld删除id为0的Helloworld对象,返回结果:" + b6);
TimeUnit.SECONDS.sleep(1);
// 测试7
boolean b7 = client.removeHelloworld(1);
logger.info("通过方法removeHelloworld删除id为1的Helloworld对象,返回结果:" + b7);
TimeUnit.SECONDS.sleep(1);
// 测试8
boolean b8 = client.removeHelloworld(2);
logger.info("通过方法removeHelloworld删除id为2的Helloworld对象,返回结果:" + b8);
TimeUnit.SECONDS.sleep(1);
} catch (TTransportException e) {
logger.error(e.getMessage());
e.printStackTrace();
} catch (TException e) {
logger.error(e.getMessage());
e.printStackTrace();
} catch (InterruptedException e) {
logger.error(e.getMessage());
e.printStackTrace();
} finally {
if (transport != null)
transport.close();
logger.info("transport关闭");
}
}
public static void main(String[] args) {
Client c = new Client();
c.startclient();
}
}
然后分别运行Server和Client即可返回执行结果。
PS:
- 因为是测试,所以用的是单线程的Server,实际上最好用TNonblockingServer。
- null值是不能返回做交互的。会抛出org.apache.thrift.TApplicationException: getHelloworld failed: unknown result。
参考(受益良多):
http://www.cnblogs.com/mumuxinfei/p/3873709.html Thrift 个人实战--初次体验Thrift
http://blog.csdn.net/amuseme_lu/article/details/6262572 Apache Thrift的简单使用
http://gemantic.iteye.com/blog/1199214 thrift的使用介绍