Thrift基础(一)Hello World
昨天写的 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的使用介绍