Thrift应用实践—基本篇
来源:互联网 发布:达内大数据培训多少钱 编辑:程序博客网 时间:2024/06/03 14:37
1.简介
Thrift作为一个RPC框架,很好的解决了跨语言调用问题,并提供灵活高效的传输方式和Server模式。在实际应用中,个人认为Thrift更多的是用于系统的解耦,负责模块间的通信,使各模块可以独立开发、测试和发布等,一般情况下无需担心跨语言调用和传输性能问题;而对外调用还是偏向于更具通用性的HTTP REST。在本帖中,我们通过编写一个基本实例来了解Thrift的远程调用。
2.准备工作
首先使用Manven创建一个Java Project,并加入Thrift依赖
<dependencies> <dependency> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId> <version>0.9.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.12</version> </dependency></dependencies>
3.编写Thrift文件
Thrift为我们提供了 IDL(Interface Definition Language) 来编写通用的服务接口文件(后缀为thrift的文件),并编译生成对应语言的模板代码,从而解决跨语言调用问题。比如我们使用的是Java,则生成java文件,使用的是Node,那就生成js文件。
假如我们向一个影院信息系统查询一部电影的数据,那我们需要一个获取影片信息的接口,客户端传入查询影片的名称,服务端处理后返回该影片的详细数据。
首先我们需要一个电影实体,用于装载相关的数据,代码如下所示。
MovieModel.thrift
//定义java命名空间,对应package的值namespace java com.thrift.simple.model struct Movie { 1:string chName //中文名 2:string enName //英文名 3:string type //类型 4:string digital //数字种类 5:i64 releaseDate //上映日期 6:i32 duration //时长 7:i32 price //售价 8:map<string, bool> screenings //场次信息,是否有票}
上述代码涉及到 IDL 支持的数据类型,有兴趣的读者可以到 http://thrift.apache.org/docs/types 简单学习一下,我们这里没有添加 required 或 optional 声明,Thrift 默认视为 required 处理,两者的区别是 required 声明的类型一定会序列化,如果你没有给它赋值,生成的 java 代码也会设置一个默认值,optional 未赋值则不会序列化。此外 IDL 只支持 java 的基本类型,像 Date 等类型是不支持的,需在代码中转换。
接下来需要一个查询电影信息的服务接口,代码如下所示。
MovieOperator.thrift
namespace java com.thrift.simple.service//引用另一个Thrift文件,并通过“文件名.数据类型”的形式调用。include "MovieModel.thrift" service MovieOperator { MovieModel.Movie getMovie(1:string chName)}
这样我们需要的Thrift文件就准备好了,输入如下命令即可生成对应的java文件。
//加上-r选项,同时生成include文件thrift -r --gen java MovieOperator.thrift
将生成的java文件放入工程对应的package下,就可以开始使用了,先简单分析一下生成的Movie和MovieOperator类。
Movie类
图3-1是Movie类的部分method,可以看到除了本身用于传输的read和write方法外,set和get,Object的equals和hashcode甚至compareTo方法都帮你写好了,在本例中我们可以直接作为Movie实体使用。
图3-1
MovieOperator类
这个类的代码看上去很多,但结构简单,主要成员如图3-2所示。其中Iface是我们刚刚定义的接口,Client和Processor分别用于客户端和服务端,里面封装着远程调用的请求和接收方法,调用过程中会用到getMovie_args和getMovie_result方法来分别获取参数和返回值。最后3个Async方法用于异步处理,本例不作详细介绍。
图3-2
4.客户端实现
现在正式编写代码,首先创建客户端的ThriftClient类,代码如下所示。
public class ThriftClient { public static void main(String[] args) { //对java的socket进行封装,定义传输的基本方法 TTransport baseTrans = new TSocket("localhost", 6060, 2000); //进一步封装,使用按帧传输的方式,服务端必须使用非阻塞类型 TTransport frameTrans = new TFramedTransport(baseTrans); //采用二进制协议,服务端协议须保持一致 TProtocol protocol = new TBinaryProtocol(frameTrans); //将配置传入Client对象 MovieOperator.Client client = new MovieOperator.Client(protocol); try { //TSocket初始化并创建Socket连接 frameTrans.open(); //远程调用getMovie方法并获取返回值 Movie movie = client.getMovie("谍影重重5"); System.out.println("get remote info ----> " + movie.toString()); } catch (TException e) { e.printStackTrace(); } finally { //别忘了关闭连接 frameTrans.close(); } }}
5.接口服务实现
编写Iface接口的实现类,也就是客户端来找服务端干的具体事情,代码如下所示。
public class MovieOperatorImpl implements MovieOperator.Iface { @Override public Movie getMovie(String chName) throws TException { //创建实体对象 Movie movie = new Movie(); //本例就不从数据库中获取了,直接塞入一份测试数据 movie.setEnName("Jason Bourne"); movie.setChName(chName); movie.setPrice(49); movie.setDigital("原版3D"); movie.setDuration(123); movie.setReleaseDate(dateTrans("2016-08-23")); movie.setType("英语"); Map<String, Boolean> map = new HashMap<String, Boolean>(); map.put("18:15", false); map.put("22:30", true); movie.setScreenings(map); return movie; } private long dateTrans(String date) { long defaultTime = 0; DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); try { defaultTime = format.parse(date).getTime(); } catch (ParseException e) { e.printStackTrace(); } return defaultTime; }}
6.服务端实现
最后编写服务端ThriftServer类,代码如下所示。
public class ThriftServer { public static final int servePort = 6060; public static void main(String[] args) { MovieOperator.Iface operator = new MovieOperatorImpl(); //将实现类的对象放入processor中维护,在客户端请求时,会映射到具体的执行方法 TProcessor processor = new MovieOperator.Processor<>(operator); try { //创建非阻塞的传输对象,对ServerSocket进行了封装 TNonblockingServerTransport nioSocket = new TNonblockingServerSocket(servePort); TNonblockingServer.Args nonBlockingArgs = new TNonblockingServer.Args(nioSocket); //放入process并设置transport和protocol的类型,注意与客户端对应 nonBlockingArgs.processor(processor); nonBlockingArgs.transportFactory(new TFramedTransport.Factory()); nonBlockingArgs.protocolFactory(new TBinaryProtocol.Factory()); //实例化Server对象 TServer server = new TNonblockingServer(nonBlockingArgs); System.out.println("service start at " + servePort); //启动并监听 server.serve(); } catch (TException e) { e.printStackTrace(); } }}
上述代码唯一存在疑惑的地方,就是TNonblockingServer的内部类Args,为什么不直接对TNonblockingServer实例化,而是借助该类配置所需的参数呢?
我们来看图6-1,TNonblockingServer只提供了一个以AbstractNonblockingServerArgs为参数的构造函数,因此必须先创建它的静态内部类Args对象。
图6-1
我们继续查看Args的继承结构,如图6-2所示。
图6-2
跟踪父类到TServer中,发现AbstractServerArgs中定义了Server所需的基本数据:Transport、Process和Protocol,也就是刚刚配置的参数属性,如图6-3所示。
图6-3
看到这里,我们已经明白大体的设计方式了,TServer作为所有Server类型的父类,除了定义基础属性和操作标准外,还提供了一个抽象的Args类用于参数的配置和管理,子类根据需要继承并扩展该类即可,如经常使用的TThreadPoolServer就是这样干的。因此在实例化其他Server时,只需创建并配置对应的Args对象即可,体现了代码的封装性和扩展性。
到此代码就完成了,我们看下运行的结果,运行结果如下。
get remote info ----> Movie(chName:谍影重重5, enName:Jason Bourne, type:英语, digital:原版3D, releaseDate:1471881600000, duration:123, price:49, screenings:{18:15=false, 22:30=true})Process finished with exit code 0
7.小结
最后再回顾一下,首先我们使用Thrift提供的IDL编写了两个Thrift文件,一个是Movie实体,另一个是MovieOperator提供服务接口,通过命令生成java代码。客户端指定好传输方式和传输协议后,调用MovieOperator的Client与服务端交互。服务端这边先实现了MovieOperator的Iface接口,编写好具体的实现代码MovieOperatorImpl,接着把MovieOperatorImpl放入MovieOperator Processor中管理,然后指定与客户端对应的Server类型、传输方式和传输协议并实例化Server对象,最后启动服务并监听指定端口。
本例源码下载 http://download.csdn.net/detail/roderick2015/9613104
Tips:
如果把Thrift生成的代码放入工程后,出现如下图所示的问题,说明你的JDK编译版本太低,在1.5中的确不能这样使用。
可以参照下面的两张图,改成高一点的版本就行啦。
- Thrift应用实践—基本篇
- Thrift—IDL基本类型
- thrift应用
- Thrift学习笔记—IDL基本类型
- Thrift学习笔记—IDL基本类型
- hBase-thrift 实践(java)
- Thrift基本使用
- Thrift基本使用
- Thrift 基本使用
- Thrift基本数据类型
- Thrift基本数据类型
- Thrift IDL基本语法
- thrift介绍及应用(二)—简单应用
- thrift介绍及应用(二)—简单应用
- thrift介绍及应用(二)—简单应用
- hadoop和hive的实践应用(三)——hive的基本应用
- Thrift应用(linux)
- thriftのjava应用
- ARM cortex a 的SDRAM (DDR)
- Impinj Spedway 在使用 Telnet 时需要用 Putty 进行连接,并且连接类型需要选 SSH
- iqiyi Thrift服务框架整体介绍
- JavaScript DOM基础知识总结
- 从零学React Native之10Text
- Thrift应用实践—基本篇
- Android中的ACCESS_MOCK_LOCATION权限使用Demo
- VB.NET机房重构代码集锦—未提供参数
- 聚集索引和非聚集索引详解
- ehcache整合spring注解方式 (亲测OK)
- 19、手机触屏事件
- 多线程学习2
- NSBundle加载xib的cell
- MyISAM和InnoDB区别