前言thrift是一个轻量级、跨语言的远程服务调用框架,最初由facebook开发,后面进入apache开源项目。它通过自身的idl中间语言, 并借助代码生成引擎生成各种主流语言的rpc服务端/客户端模板代码。
thrift支持多种不同的编程语言,包括c++、java、python、php、ruby等,本系列主要讲述基于java语言的thrift的配置方式和具体使用。
正文thrift的技术栈
thrift对软件栈的定义非常的清晰, 使得各个组件能够松散的耦合, 针对不同的应用场景, 选择不同是方式去搭建服务。
thrift软件栈分层从下向上分别为:传输层(transport layer)、协议层(protocol layer)、处理层(processor layer)和服务层(server layer)。
传输层(transport layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说tcp/ip传输等。协议层(protocol layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说json、xml、二进制数据等。处理层(processor layer):处理层是由具体的idl(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的handler进行处理。服务层(server layer):整合上述组件,提供具体的网络线程/io服务模型,形成最终的服务。thrift的特性
(一) 开发速度快
通过编写rpc接口thrift idl文件,利用编译生成器自动生成服务端骨架(skeletons)和客户端桩(stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。
服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序(handler)即实现类即可。客户端:只需要拷贝idl定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。(二) 接口维护简单
通过维护thrift格式的idl(接口描述语言)文件(注意写好注释),即可作为给client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且thrift协议可灵活支持接口的可扩展性。
(三) 学习成本低
因为其来自google protobuf开发团队,所以其idl文件风格类似google protobuf,且更加易读易懂;特别是rpc服务接口的风格就像写一个面向对象的class一样简单。
初学者只需参照:位有符号整数i16: 16位有符号整数i32: 32位有符号整数i64: 64位有符号整数double: 64位浮点数string: utf-8编码的字符串binary: 二进制串结构体类型:struct: 定义的结构体对象容器类型:list: 有序元素列表set: 无序无重复元素集合map: 有序的key/value集合异常类型:exception: 异常类型服务类型:service: 具体对应服务的类thrift的协议
thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本(text)和二进制(binary)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:
tbinaryprotocol:二进制编码格式进行数据传输tcompactprotocol:高效率的、密集的二进制编码格式进行数据传输tjsonprotocol: 使用json文本的数据编码协议进行数据传输tsimplejsonprotocol:只提供json只写的协议,适用于通过脚本语言解析thrift的传输层
常用的传输层有以下几种:
tsocket:使用阻塞式i/o进行传输,是最常见的模式tnonblockingtransport:使用非阻塞方式,用于构建异步客户端tframedtransport:使用非阻塞方式,按块的大小进行传输,类似于java中的niothrift的服务端类型
tsimpleserver:单线程服务器端,使用标准的阻塞式i/otthreadpoolserver:多线程服务器端,使用标准的阻塞式i/otnonblockingserver:单线程服务器端,使用非阻塞式i/othshaserver:半同步半异步服务器端,基于非阻塞式io读写和多线程工作任务处理tthreadedselectorserver:多线程选择器服务器端,对thshaserver在异步io模型上进行增强thrift入门示例
(一) 编写thrift idl文件
a). 下载0.10.0的thrift idl编译器,下载地址:: string username)
}
d). 使用代码生成工具生成代码,执行以下命令:
thrift -gen java hello.thrift
e). 由于未指定代码生成的目标目录,生成的类文件默认存放在gen-java目录下。这里生成一个helloworldservice.java类文件,文件大小超过数千行,下面截取一部分核心代码。
public class helloworldservice {
public interface iface {
public string say(string username) throws org.apache.thrift.texception;
}
public interface asynciface {
public void say(string username, org.apache.thrift.async.asyncmethodcallback resulthandler) throws org.apache.thrift.texception;
}
public static class client extends org.apache.thrift.tserviceclient implements iface {
public static class factory implements org.apache.thrift.tserviceclientfactory {
public factory() {
}
public client getclient(org.apache.thrift.protocol.tprotocol prot) {
return new client(prot);
}
public client getclient(org.apache.thrift.protocol.tprotocol iprot, org.apache.thrift.protocol.tprotocol oprot) {
return new client(iprot, oprot);
}
}
public client(org.apache.thrift.protocol.tprotocol prot) {
super(prot, prot);
}
public client(org.apache.thrift.protocol.tprotocol iprot, org.apache.thrift.protocol.tprotocol oprot) {
super(iprot, oprot);
}
public string say(string username) throws org.apache.thrift.texception {
send_say(username);
return recv_say();
}
// 省略.....
}
public static class asyncclient extends org.apache.thrift.async.tasyncclient implements asynciface {
public static class factory implements org.apache.thrift.async.tasyncclientfactory {
private org.apache.thrift.async.tasyncclientmanager clientmanager;
private org.apache.thrift.protocol.tprotocolfactory protocolfactory;
public factory(org.apache.thrift.async.tasyncclientmanager clientmanager, org.apache.thrift.protocol.tprotocolfactory protocolfactory) {
this.clientmanager = clientmanager;
this.protocolfactory = protocolfactory;
}
public asyncclient getasyncclient(org.apache.thrift.transport.tnonblockingtransport transport) {
return new asyncclient(protocolfactory, clientmanager, transport);
}
}
public asyncclient(org.apache.thrift.protocol.tprotocolfactory protocolfactory, org.apache.thrift.async.tasyncclientmanager clientmanager, org.apache.thrift.transport.tnonblockingtransport transport) {
super(protocolfactory, clientmanager, transport);
}
public void say(string username, org.apache.thrift.async.asyncmethodcallback resulthandler) throws org.apache.thrift.texception {
checkready();
say_call method_call = new say_call(username, resulthandler, this, ___protocolfactory, ___transport);
this.___currentmethod = method_call;
___manager.call(method_call);
}
// 省略.....
}
// 省略.....
}
对于开发人员而言,使用原生的thrift框架,仅需要关注以下四个核心内部接口/类:iface, asynciface, client和asyncclient。
iface:服务端通过实现helloworldservice.iface接口,向客户端的提供具体的同步业务逻辑。asynciface:服务端通过实现helloworldservice.iface接口,向客户端的提供具体的异步业务逻辑。client:客户端通过helloworldservice.client的实例对象,以同步的方式访问服务端提供的服务方法。asyncclient:客户端通过helloworldservice.asyncclient的实例对象,以异步的方式访问服务端提供的服务方法。(二) 新建maven工程
a). 新建maven工程,引入thrift的依赖,这里使用的是版本0.10.0。
org.apache.thrift
libthrift
0.10.0
b). 将生成类的helloworldservice.java源文件拷贝进项目源文件目录中,并实现helloworldservice.iface的定义的say()方法。
helloworldserviceimpl.java
public class helloworldserviceimpl implements helloworldservice.iface {
@override
public string say(string username) throws texception {
return hello + username;
}
}
c). 服务器端程序编写:
simpleserver.java
public class simpleserver {
public static void main(string[] args) throws exception {
serversocket serversocket = new serversocket(serverconfig.server_port);
tserversocket servertransport = new tserversocket(serversocket);
helloworldservice.processor processor =
new helloworldservice.processor(new helloworldserviceimpl());
tbinaryprotocol.factory protocolfactory = new tbinaryprotocol.factory();
tsimpleserver.args targs = new tsimpleserver.args(servertransport);
targs.processor(processor);
targs.protocolfactory(protocolfactory);
// 简单的单线程服务模型 一般用于测试
tserver tserver = new tsimpleserver(targs);
system.out.println(running simple server);
tserver.serve();
}
}
d). 客户端程序编写:
simpleclient.java
public class simpleclient {
public static void main(string[] args) {
ttransport transport = null;
try {
transport = new tsocket(serverconfig.server_ip, serverconfig.server_port, serverconfig.timeout);
tprotocol protocol = new tbinaryprotocol(transport);
helloworldservice.client client = new helloworldservice.client(protocol);
transport.open();
string result = client.say(leo);
system.out.println(result =: + result);
} catch (texception e) {
e.printstacktrace();
} finally {
if (null != transport) {
transport.close();
}
}
}
}
e). 运行服务端程序,服务端在指定端口监听客户端的连接请求,控制台输出启动日志:
image
f). 运行客户端程序,客户端通过网络请求helloworldservice的say()方法的具体实现,控制台输出返回结果:
这里使用的一个基于单线程同步的简单服务模型,一般仅用于入门学习和测试!
总结本文对thrift的概念做了相关介绍,体验了一番thrift程序如何编写!