为了支持多种措辞,Apache Thrift有一套自己的接口定义措辞,并且通过Apache Thrift的代码天生程序,能够天生各种编程措辞的代码。这样是担保各种措辞进行通讯的条件条件。为了能够实现大略的Apache Thrift实例,首先我们就须要讲解一下Apache Thrift的IDL。
2-1、Thrift代码天生程序安装如果您是在windows环境下运行进行Apache Thrift的试验,那么您无需安装任何工具,直接下载Apache Thrift在windows下的代码天生程序http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.exe(在这篇文章写作时,利用的是Apache Thrift的0.9.3版本);如果您运行在Linux系统下,那么下载http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.tar.gz,并进行编译、安装(过程很大略,这里就不再赘述了)。安装后记得添加运行位置到环境变量中。
以下是一个大略的IDL文件定义:

# 命名空间的定义 把稳‘java’的关键字namespace java testThrift.iface# 构造体定义struct Request { 1:required string paramJSON; 2:required string serviceName;}# 另一个构造体定义struct Reponse { 1:required RESCODE responeCode; 2:required string responseJSON;}# 非常描述定义exception ServiceException { 1:required EXCCODE exceptionCode; 2:required string exceptionMess;}# 列举定义enum RESCODE { _200=200; _500=500; _400=400;}# 另一个列举enum EXCCODE { PARAMNOTFOUND = 2001; SERVICENOTFOUND = 2002;}# 做事定义service HelloWorldService { Reponse send(1:Request request) throws (1:ServiceException e);}
以上IDL文件是可以直接用来天生各种措辞的代码的。下面给出常用的各种不同措辞的代码天生命令:
# 天生javathrift-0.9.3 -gen java ./demoHello.thrift# 天生c++thrift-0.9.3 -gen cpp ./demoHello.thrift# 天生phpthrift-0.9.3 -gen php ./demoHello.thrift# 天生node.jsthrift-0.9.3 -gen js:node ./demoHello.thrift# 天生c#thrift-0.9.3 -gen csharp ./demoHello.thrift# 您可以通过以下命令查看天生命令的格式thrift-0.9.3 -help1234567891011121314151617
2-2-1、基本类型
基本类型便是:不管哪一种措辞,都支持的数据形式表现。Apache Thrift中支持以下几种基本类型:
bool: 布尔值 (true or false), one bytebyte: 有符号字节i16: 16位有符号整型i32: 32位有符号整型i64: 64位有符号整型double: 64位浮点型string: 字符串/字符数组binary: 二进制数据(在java中表现为java.nio.ByteBuffer)2-2-2、struct构造在面向工具措辞中,表现为“类定义”;在弱类型措辞、动态措辞中,表现为“构造/构造体”。定义格式如下:
struct <构造体名称> { <序号>:[字段性子] <字段类型> <字段名称> [= <默认值>] [;|,]}
实例:
struct Request { 1:required binary paramJSON; 2:required string serviceName 3:optional i32 field1 = 0; 4:optional i64 field2, 5: list<map<string , string>> fields3}
构造体名称:可以按照您的业务需求,给定不同的名称(区分大小写)。但是要把稳,一组IDL定义文件中构造体名称不能重复,且不能利用IDL已经占用的关键字(例如required 、struct 等单词)。序号:序号非常主要。正整数,按照顺序排列利用。这个属性在Apache Thrift进行序列化的时候被利用。字段性子:包括两种关键字:required 和 optional,如果您不指定,那么系统会默认为required。required表示这个字段必须有值,并且Apache Thrift在进行序列化时,这个字段都会被序列化;optional表示这个字段不一定有值,且Apache Thrift在进行序列化时,这个字段只有有值的情形下才会被序列化。字段类型:在struct中,字段类型可以是某一个根本类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的列举。字段的类型是必须指定的。字段名称:字段名称区分大小写,不能重复,且不能利用IDL已经占用的关键字(例如required 、struct 等单词)。默认值:您可以为某一个字段指定默认值(也可以不指定)。结束符:在struct中,支持两种结束符,您可以利用“;”或者“,”。当然您也可以不该用结束符(Apache Thrift代码天生程序,会自己识别到)2-2-3、containers凑集/容器
Apache Thrift支持三种类型的容器,容器在各种编程措辞中普遍存在:
list< T >:有序列表(JAVA中表现为ArrayList),T可以是某种根本类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的列举。有序列表中的元素许可重复。set< T >:无序元素凑集(JAVA中表现为HashSet),T可以是某种根本类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的列举。无序元素凑集中的元素不许可重复,一旦重复后一个元素将覆盖前一个元素。map2-2-4、enmu列举enum <列举名称> { <列举字段名> = <列举值>[;|,]}123
示例如下:
enum RESCODE { _200=200; _500=500; _400=400;}12345
2-2-5、常量定义
Apache Thrift许可定义常量。常量的关键字为“const”,常量的类型可以是Apache Thrift的根本类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的列举。示例如下:
const i32 MY_INT_CONST = 111111; const i64 MY_LONG_CONST = 11111122222222333333334444444;const RESCODE MY_RESCODE = RESCODE._200;12345
2-2-6、exception 非常
Apache Thrift的exception,紧张在定义做事接口时利用。其定义办法类似于struct(您可以理解成,把struct关键字换成exception关键字即可),示例如下:
exception ServiceException { 1:required EXCCODE exceptionCode; 2:required string exceptionMess;}1234
2-2-7、service 做事接口
Apache Thrift中最主要的IDL定义之一。在后续的代码天生阶段,通过IDL定义的这些做事将构成Apache Thrift客户端调用Apache Thrift做事真个基本远端过程。service做事接口的定义形式如下所示:
service <做事名称> { <void | 返回指类型> <做事方法名>([<入参序号>:[required | optional] <参数类型> <参数名> ...]) [throws ([<非常序号>:[required | optional] <非常类型> <非常参数名>...])]}123
做事名称:做事名可以按照您的业务需求自行制订,把稳做事名是区分大小写的。IDL中做事名称只有两个限定,便是不能重复利用相同的名称,不能利用IDL已经占用的关键字(例如required 、struct 等单词)。返回值类型:如果这个调用方法没有返回类型,那么可以关键字“void”; 可以是Apache Thrift的根本类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的列举。做事方法名:做事方法名可以根据您的业务需求自定制订,把稳区分大小写。在同一个做事中,不能重复利用一个做事方法名命名多个方法(一定要把稳),不能利用IDL已经占用的关键字。做事方法参数:<入参序号>:[required | optional] <参数类型> <参数名>。把稳和struct中的字段定义相似,可以指定required或者optional;如果不指定则系统默认为required 。如果一个做事方法中有多个参数名,那么这些参数名称不能重复。做事方法非常:throws ([<非常序号>:[required | optional] <非常类型> <非常参数名>。throws关键字是做事方法非常定义的开始点。在throws关键字后面,可以定义1个或者多个不同的非常类型。
Apache Thrift做事定义的示例如下:
service HelloWorldService { Reponse send(1:Request request) throws (1:ServiceException e);}123
2-2-8、namespace命名空间
Apache Thrift支持为不同措辞制订不同的命名空间:
namespace java testThrift.ifacenamespace php testThrift.ifacenamespace cpp testThrift.iface12345
2-2-9、注释
Apache Thrift 支持多种风格的注释。这是为了适应不同措辞背景的开拓者:
/ 注释办法1:/// 注释办法2# 注释办法31234567
2-2-10、include关键字
如果您的全体工程中有多个IDL定义文件(IDL定义文件的文件名可以随便取)。那么您可以利用include关键字,在IDL定义文件A中,引入一个其他的IDL文件:
include "other.thrift"1
请把稳,一定利用双引号(不要用成中文的双引号咯),并且不该用“;”或者“,”结束符。
以上便是IDL基本的语法了,由于篇幅缘故原由不可能把每种语法、每一个细节都讲到,但是以上的语法要点已经足够您编辑一个适应业务的,灵巧的IDL定义了。如果您须要理解更详细的Thrift IDL语法,可以参考官方文档的讲述:http://thrift.apache.org/docs/idl
2-3、最大略的Thrift代码定义Thrift中业务接口HelloWorldService.Iface的实现:package testThrift.impl;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.thrift.TException;import testThrift.iface.HelloWorldService.Iface;import testThrift.iface.RESCODE;import testThrift.iface.Reponse;import testThrift.iface.Request;/ 我们定义了一个HelloWorldService.Iface接口的详细实现。<br> 把稳,这个父级接口:HelloWorldService.Iface,是由thrift的代码天生工具天生的<br> 要运行这段代码,请导入maven-log4j的支持。否则修正LOGGER.info方法 @author yinwenjie /public class HelloWorldServiceImpl implements Iface { / 日志 / private static final Log LOGGER = LogFactory.getLog(HelloWorldServiceImpl.class); / 在接口定义中,只有一个方法须要实现。<br> HelloWorldServiceImpl.send(Request request) throws TException <br> 您可以理解成这个接口的方法接管客户真个一个Request工具,并且在处理完成后向客户端返回一个Reponse工具<br> Request工具和Reponse工具都是由IDL定义的构造,并通过“代码天生工具”天生相应的JAVA代码。 / @Override public Reponse send(Request request) throws TException { / 这里便是进行详细的业务处理了。 / String json = request.getParamJSON(); String serviceName = request.getServiceName(); HelloWorldServiceImpl.LOGGER.info("得到的json:" + json + " ;得到的serviceName: " + serviceName); // 布局返复书息 Reponse response = new Reponse(); response.setResponeCode(RESCODE._200); response.setResponseJSON("{\"user\":\"yinwenjie\"}"); return response; } }
各位可以看到,上面一段代码中详细业务和过程和普通的业务代码没有任何差异。乃至这段代码的实现都不知道自己将被Apache Thrift框架中的客户端调用。
然后我们开始书写Apache Thrift的做事器端代码:package testThrift.man;import java.util.concurrent.Executors;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.log4j.BasicConfigurator;import org.apache.thrift.TProcessor;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.server.TThreadPoolServer;import org.apache.thrift.server.TThreadPoolServer.Args;import org.apache.thrift.transport.TServerSocket;import testThrift.iface.HelloWorldService;import testThrift.iface.HelloWorldService.Iface;import testThrift.impl.HelloWorldServiceImpl;public class HelloBoServerDemo { static { BasicConfigurator.configure(); } / 日志 / private static final Log LOGGER =LogFactory.getLog(HelloBoServerDemo.class); public static final int SERVER_PORT = 9111; public void startServer() { try { HelloBoServerDemo.LOGGER.info("看到这句就解释thrift做事端准备事情 ...."); // 做事实行掌握器(只假如调度做事的详细实现该如何运行) TProcessor tprocessor = new HelloWorldService.Processor<Iface>(new HelloWorldServiceImpl()); // 基于壅塞式同步IO模型的Thrift做事,正式生产环境不建议用这个 TServerSocket serverTransport = new TServerSocket(HelloBoServerDemo.SERVER_PORT); // 为这个做事器设置对应的IO网络模型、设置利用的格式封装、设置线程池参数 Args tArgs = new Args(serverTransport); tArgs.processor(tprocessor); tArgs.protocolFactory(new TBinaryProtocol.Factory()); tArgs.executorService(Executors.newFixedThreadPool(100)); // 启动这个thrift做事 TThreadPoolServer server = new TThreadPoolServer(tArgs); server.serve(); } catch (Exception e) { HelloBoServerDemo.LOGGER.error(e); } } / @param args / public static void main(String[] args) { HelloBoServerDemo server = new HelloBoServerDemo(); server.startServer(); }}
以上的代码有几点须要解释:
TBinaryProtocol:这个类代码Apache Thrift特有的一种二进制描述格式。它的特点是传输单位数据量所利用的传输量更少。Apache Thrift还支持多种数据格式,例如我们熟习的JSON格式。后文我们将详细先容Apache Thrift中的数据格式。tArgs.executorService():是不是以为这个executorService很熟习,是的这个便是JAVA JDK 1.5+ 后java.util.concurrent包供应的异步任务调度做事接口,Java标准线程池ThreadPoolExecutor便是它的一个实现。server.serve(),由于是利用的同步壅塞式网络IO模型,以是这个运用程序的主线程实行到这句话往后就会保持壅塞状态了。不过下层网络状态不涌现缺点,这个线程就会一贯停在这里。其余,同HelloWorldServiceImpl 类中的代码,请利用Log4j。如果您的测试工程里面没有Log4j,请改用System.out。
接下来我们进行最大略的Apache Thrift Client的代码编写:package testThrift.client;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.log4j.BasicConfigurator;import org.apache.thrift.protocol.TBinaryProtocol;import org.apache.thrift.protocol.TProtocol;import org.apache.thrift.transport.TSocket;import testThrift.iface.HelloWorldService;import testThrift.iface.Reponse;import testThrift.iface.Request;/ 同样是基于同步壅塞模型的thrift client。 @author yinwenjie /public class HelloClient { static { BasicConfigurator.configure(); } / 日志 / private static final Log LOGGER = LogFactory.getLog(HelloClient.class); public static final void main(String[] args) throws Exception { // 做事器所在的IP和端口 TSocket transport = new TSocket("127.0.0.1", 9111); TProtocol protocol = new TBinaryProtocol(transport); // 准备调用参数 Request request = new Request("{\"param\":\"field1\"}", "\\mySerivce\\queryService"); HelloWorldService.Client client = new HelloWorldService.Client(protocol); // 准备传输 transport.open(); // 正式调用接口 Reponse reponse = client.send(request); // 一定要记住关闭 transport.close(); HelloClient.LOGGER.info("response = " + reponse); }}
Thrift客户端所利用的网络IO模型,必须要与Thrift做事器端所利用的网络IO模型同等。也便是说做事器端如果利用的是壅塞式同步IO模型,那么客户端就必须利用壅塞式同步IO模型。Thrift客户端所利用的封装格式,必须要与Thrift做事器端所利用的封装格式一贯。也便是说做事器端如果利用的是二进制流的格式TBinaryProtocol,那么客户端就必须同样利用二进制刘的格式TBinaryProtocol。其它的代码要么便是由IDL定义并由Thrift的代码天生工具天生;要么就不是主要的代码,所以为了节约篇幅就没有必要再贴出来了。以下是运行效果。做事器端运行效果
请把稳做事器端在收到客户端要求后的运行办法:取出一条线程池中的线程,并且运行这个做事接口的详细实现。接下来我们立时先容Apache Thrift的事情细节。
(接下文)