Thrift
是一个轻量级、跨语言的远程服务调用框架,最初由Facebook
开发,后面进入Apache
开源项目。它经过自身的IDL
中间语言, 并借助代码生成引擎生成各类主流语言的RPC
服务端/客户端模板代码。java
Thrift
支持多种不一样的编程语言,包括C++
、Java
、Python
、PHP
、Ruby
等,本系列主要讲述基于Java
语言的Thrift
的配置方式和具体使用。apache
Thrift
对软件栈的定义很是的清晰, 使得各个组件可以松散的耦合, 针对不一样的应用场景, 选择不一样是方式去搭建服务。编程
Thrift
软件栈分层从下向上分别为:传输层(Transport Layer
)、协议层(Protocol Layer
)、处理层(Processor Layer
)和服务层(Server Layer
)。后端
传输层(Transport Layer
):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;好比说TCP/IP
传输等。缓存
协议层(Protocol Layer
):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;好比说JSON
、XML
、二进制数据等。bash
处理层(Processor Layer
):处理层是由具体的IDL
(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler
进行处理。服务器
服务层(Server Layer
):整合上述组件,提供具体的网络线程/IO服务模型,造成最终的服务。markdown
经过编写RPC
接口Thrift IDL
文件,利用编译生成器自动生成服务端骨架(Skeletons
)和客户端桩(Stubs
)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工做。网络
Handler
)即实现类便可。IDL
定义好的客户端桩和服务对象,而后就像调用本地对象的方法同样调用远端服务。经过维护Thrift
格式的IDL(接口描述语言)文件(注意写好注释),便可做为给Client
使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且Thrift
协议可灵活支持接口的可扩展性。多线程
由于其来自Google Protobuf
开发团队,因此其IDL
文件风格相似Google Protobuf
,且更加易读易懂;特别是RPC
服务接口的风格就像写一个面向对象的Class
同样简单。
初学者只需参照:thrift.apache.org/,一个多小时就能够理解Thrift IDL
文件的语法使用。
Thrift
支持C++
、 Java
、Python
、PHP
、Ruby
、Erlang
、Perl
、Haskell
、C#
、Cocoa
、JavaScript
、Node.js
、Smalltalk
等多种语言,便可生成上述语言的服务器端和客户端程序。
对于咱们常用的Java
、PHP
、Python
、C++
支持良好,虽然对iOS
环境的Objective-C
(Cocoa
)支持稍逊,但也彻底知足咱们的使用要求。
Thrift
在不少开源项目中已经被验证是稳定和高效的,例如Cassandra
、Hadoop
、HBase
等;国外在Facebook
中有普遍使用,国内包括百度、美团小米、和饿了么等公司。
Thrift 脚本可定义的数据类型包括如下几种类型:
Thrift
可让用户选择客户端与服务端之间传输通讯协议的类别,在传输协议上整体划分为文本(text
)和二进制(binary
)传输协议。为节约带宽,提升传输效率,通常状况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这须要根据项目/产品中的实际需求。经常使用协议有如下几种:
JSON
文本的数据编码协议进行数据传输JSON
只写的协议,适用于经过脚本语言解析经常使用的传输层有如下几种:
I/O
进行传输,是最多见的模式Java
中的NIO
I/O
I/O
I/O
IO
读写和多线程工做任务处理THsHaServer
在异步IO
模型上进行加强a). 下载0.10.0
的Thrift IDL
编译器,下载地址:http://thrift.apache.org/docs/install。 经过编译生成器生成.java
接口的类文件。
b). 下载Windows
安装环境的.exe
文件,将thrift.exe
的路径加入环境变量中。在Idea
上安装Thrift
编辑插件。
c). 编写hello.thrift
的IDL
文件:
service HelloWorldService { string say(1: 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<String> 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<Client> { 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<AsyncClient> { 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<String> 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
。
HelloWorldService.Iface
接口,向客户端的提供具体的同步业务逻辑。HelloWorldService.Iface
接口,向客户端的提供具体的异步业务逻辑。HelloWorldService.Client
的实例对象,以同步的方式访问服务端提供的服务方法。HelloWorldService.AsyncClient
的实例对象,以异步的方式访问服务端提供的服务方法。a). 新建maven
工程,引入thrift
的依赖,这里使用的是版本0.10.0
。
<dependency> <groupId>org.apache.thrift</groupId> <artifactId>libthrift</artifactId> <version>0.10.0</version> </dependency> 复制代码
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<HelloWorldService.Iface>(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). 运行服务端程序,服务端在指定端口监听客户端的链接请求,控制台输出启动日志:
f). 运行客户端程序,客户端经过网络请求HelloWorldService
的say()
方法的具体实现,控制台输出返回结果:
这里使用的一个基于单线程同步的简单服务模型,通常仅用于入门学习和测试!
本文对Thrift
的概念作了相关介绍,体验了一番thrift
程序如何编写!
欢迎关注技术公众号: 零壹技术栈
本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。