由浅⼊深了解Thrift(⼀)——Thrift介绍与⽤法
⼀、  Thrift简单介绍
1.1、  Thrift是什么?能做什么?
Thrift是Facebook于2007年开发的跨语⾔的rpc服框架,提供多语⾔的编译功能,并提供多种服务器⼯作模式;⽤户通过Thrift的IDL(接⼝定义语⾔)来描述接⼝函数及数据类型,然后通过Thrift的编译环境⽣成各种语⾔类型的接⼝⽂件,⽤户可以根据⾃⼰的需要采⽤不同的语⾔开发客户端代码和服务器端代码。
例如,我想开发⼀个快速计算的RPC服务,它主要通过接⼝函数getInt对外提供服务,这个RPC服务的getInt函数使⽤⽤户传⼊的参数,经过复杂的计算,计算出⼀个整形值返回给⽤户;服务器端使⽤java语⾔开发,⽽调⽤客户端可以是java、c、python等语⾔开发的程序,在这种应⽤场景下,我们只需要使⽤Thrift的IDL描述⼀下getInt函数(以.thrift为后缀的⽂件),然后使⽤Thrift的多语⾔编译功能,将这个IDL⽂件编译成C、java、python⼏种语⾔对应的“特定语⾔接⼝⽂件”(每种语⾔只需要⼀条简单的命令即可编译完成),这样拿到对应语⾔的“特定语⾔接⼝⽂件”之后,就可以开发客户端和服务器端的代码了,开发过程中只要接⼝不变,客户端和服务器端的开发可以独⽴的进⾏。
Thrift为服务器端程序提供了很多的⼯作模式,例如:线程池模型、⾮阻塞模型等等,可以根据⾃⼰的实际应⽤场景选择⼀种⼯作模式⾼效地对外提供服务;
1.2、  Thrift的相关⽹址和资料:
(3)  Thrift官⽅的IDL⽰例⽂件(⾃⼰写IDL⽂件时可以此为参考):
(4)  各种环境下搭建Thrift的⽅法:
该页⾯中共提供了CentOS\Ubuntu\OS X\Windows⼏种环境下的搭建Thrift环境的⽅法。
⼆、  Thrift的使⽤
Thrift提供跨语⾔的服务框架,这种跨语⾔主要体现在它对多种语⾔的编译功能的⽀持,⽤户只需要使⽤IDL描述好接⼝函数,只需要⼀条简单的命令,Thrift就能够把按照IDL格式描述的接⼝⽂件翻译成各种语⾔版本。其实,说搭建Thrift环境的时候,实际上最⿇烦的就是搭建Thrift的编译环境,Thrift的编译和通常的编译⼀样经过词法分析、语法分析等等最终⽣成对应语⾔的源码⽂件,为了能够⽀持对各种语⾔的编译,你需要下载各种语⾔对应的编译时使⽤的包;
2.1、  搭建Thrift的编译环境
本节主要介绍如何搭建Unix编译环境,搭建时有以下要求:
基本要求:
G++、boost、lex、yacc
源码安装要求:
如果使⽤源码安装的⽅式,则还需要下列⼯具:
Autoconf、automake、libtool、pkg-config、lex和yacc的开发版、libssl-dev
语⾔要求:
搭建C++编译环境:boost、libevent、zlib
搭建java编译环境:jdk、ApacheAnt
具体搭建环境时可以参考“⼀”中所列官⽹的安装⽅法。
2.2、  搭建JAVA下Thrift开发环境
在java环境下开发thrift的客户端或者服务器程序⾮常简单,只需在⼯程⽂件中加上下⾯三个jar包(版本可能随时有更新):
libthrift-0.9.1.jar
slf4j-api-1.7.5.jar
slf4j-simple.jar
2.3、  编写IDL⽂件
使⽤Thrift开发程序,⾸先要做的事情就是使⽤IDL对接⼝进⾏描述, 然后再使⽤Thrift的多语⾔编译能⼒将接⼝的描述⽂件编译成对应语⾔的版本,本⽂中将IDL对接⼝的描述⽂件称为“Thrift⽂件”。
(1)  编写Thrift⽂件
使⽤IDL对接⼝进⾏描述的thrift⽂件命名⼀般都是以“.thrift”作为后缀:XXX.thrift,可以在该⽂件的开头为该⽂件加上命名空间限制,格式为:namespace语⾔ 命名空间的名字;例如:
st.service
IDL⽂件中对所有接⼝函数的描述都放在service中,service的名字可以⾃⼰指定,该名字也将被⽤作⽣成的特定语⾔接⼝⽂件的名字,接⼝函数需要对参数使⽤序号标号,除最后⼀个接⼝函数外,要以“,”结束对函数的描述。
例如,下⾯⼀个IDL描述的Thrift⽂件(该Thrift⽂件的⽂件名为:test_service.thrift)的全部内容:
namespace st.service
include "thrift_datatype.thrift"
service TestThriftService
{
/**
*value 中存放两个字符串拼接之后的字符串
*/
thrift_datatype.ResultStr getStr(1:string srcStr1, 2:string srcStr2),
thrift_datatype.ResultInt getInt(1:i32 val)
}
代码2.1
这⾥的TestThriftService就被⽤作⽣成的特定语⾔的⽂件名,例如我想⽤该Thrift⽂件⽣成⼀个java版本的接⼝⽂件,那么⽣成的java⽂件名就是:TestThriftService.java。
(1)  编写IDL⽂件时需要注意的问题
[1]函数的参数要⽤数字依序标好,序号从1开始,形式为:“序号:参数名”;
[2]每个函数的最后要加上“,”,最后⼀个函数不加;
[3]在IDL中可以使⽤/*……*/添加注释
(2)  IDL⽀持的数据类型
IDL⼤⼩写敏感,它共⽀持以下⼏种基本的数据类型:
[1]string, 字符串类型,注意是全部⼩写形式;例如:string aString
[2]i16, 16位整形类型,例如:i16 aI16Val;
[3]i32,32位整形类型,对应C/C++/java中的int类型;例如:      I32  aIntVal
[4]i64,64位整形,对应C/C++/java中的long类型;例如:I64 aLongVal
[5]byte,8位的字符类型,对应C/C++中的char,java中的byte类型;例如:byte aByteVal
[6]bool, 布尔类型,对应C/C++中的bool,java中的boolean类型; 例如:bool aBoolVal
[7]double,双精度浮点类型,对应C/C++/java中的double类型;例如:double aDoubleVal
[8]void,空类型,对应C/C++/java中的void类型;该类型主要⽤作函数的返回值,例如:void testVoid(),
除上述基本类型外,ID还⽀持以下类型:
[1]map,map类型,例如,定义⼀个map对象:map<i32, i32> newmap;
[2]set,集合类型,例如,定义set<i32>对象:set<i32> aSet;
[3]list,链表类型,例如,定义⼀个list<i32>对象:list<i32> aList;
(3)  在Thrift⽂件中⾃定义数据类型
在IDL中⽀持两种⾃定义类型:枚举类型和结构体类型,具体如下:
[1]enum, 枚举类型,例如,定义⼀个枚举类型:java调用python模型
enum Numberz
{
ONE = 1,
TWO,
THREE,
FIVE = 5,
SIX,
EIGHT = 8
}
注意,枚举类型⾥没有序号
[2]struct,⾃定义结构体类型,在IDL中可以⾃⼰定义结构体,对应C中的struct,c++中的struct和class,java中的class。例如:
struct TestV1 {
1: i32 begin_in_both,
3: string old_string,
12: i32 end_in_both
}
注意,在struct定义结构体时需要对每个结构体成员⽤序号标识:“序号: ”。
(4)  定义类型别名
Thrift的IDL⽀持C/C++中类似typedef的功能,例如:
typedefi32  Integer
就可以为i32类型重新起个名字Integer。
2.4、  ⽣成Thrift服务接⼝⽂件
搭建Thrift编译环境之后,使⽤下⾯命令即可将IDL⽂件编译成对应语⾔的接⼝⽂件:
thrift --gen <language> <Thrift filename>
例如:如果使⽤上⾯的thrift⽂件(见上⾯的代码2.1):test_service.thrift⽣成⼀个java语⾔的接⼝⽂件,则只需在搭建好thrift编译环境的机⼦上,执⾏如下命令即可:
thrift --gen java test_service.thrift
这⾥,我直接在test_service.thrift⽂件所在的⽬录下执⾏的命令,所以直接使⽤⽂件名即可(如图2.1的标号1所⽰),如果不在
test_service.thrift所在的⽬录中,则需要具体指明该⽂件所在的路径。
图2.1
如图2.1 中标号2所⽰,⽣成的gen-java的⽬录,⽬录下⾯有com、test、service三级⽬录,这三级⽬录也是根据test_service.thrift⽂件中命名空间的名字:st.service⽣成的,进⼊⽬录之后可以看到⽣成的java语⾔的接⼝⽂件名为:TestThriftService.java,这个⽂件的名字也是根据test_service.thrift⽂件的service名字来⽣成的(见代码2.1)。
2.5、  编写服务器端的java代码
编写thrift服务器程序需要⾸先完成下⾯两步⼯作:
(1)先将2.2节中的三个jar包添加到⼯程⾥,如图2.2的标号2所⽰。
(2)将⽣成的java接⼝⽂件TestThriftService.java拷贝到⾃⼰的⼯程⽂件中,如图2.2的标号1所⽰。
图2.2
服务端程序需实现TestThriftService.Iface接⼝,在实现接⼝中完成⾃⼰要提供的服务,服务器端对服
务接⼝实现的代码如下所⽰:
st.service;
import org.apache.thrift.TException;
public class TestThriftServiceImpl implements TestThriftService.Iface
{
@Override
public String getStr(String srcStr1, String srcStr2) throws TException {
long startTime = System.currentTimeMillis();
String res = srcStr1 + srcStr2;
long stopTime = System.currentTimeMillis();
System.out.println("[getStr]time interval: " + (stopTime-startTime));
return res;
}
@Override
public int getInt(int val) throws TException {
long startTime = System.currentTimeMillis();
int res = val * 10;
long stopTime = System.currentTimeMillis();
System.out.println("[getInt]time interval: " + (stopTime-startTime));
return res;
}
}
代码2.2
服务器端启动thrift服务框架的程序如下所⽰,在本例中服务器采⽤TNonblockingServer⼯作模式: