Apache Thrift的简单使用
----------------------
1. 简单介绍
Thrift是Facebook的一个开源项目,主要是一个跨语言的服务开发框架。它有一个代码生成器来对它所定义的IDL定义文件自动生成服务代码框架。用户只要在其之前进行二次开发就行,对于底层的RPC通讯等都是透明的。目前它支持的语言有C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, and OCaml.
2. 下载与安装
可以在http://incubator.apache.org/thrift/download/去下载它的最新版本,目前最新版本是0.5.0。另外你也可以check出它的svn,方法如下:
svn co http://svn.apache.org/repos/asf/thrift/trunk thrift
cd thrift
在它的jira中看到,它的0.6版本也很快就会出来了。
我的本本是debian 6.0,如果用ubuntu的兄弟安装方法也是一样的
- tar -zxvf thrift-0.5.0.tar.gz
- cd thrift-0.5.0
- ./configure
- make
- sudo make install
这时thrift的代码生成器和一些库文件就生成好了。
你可以用如下命令看一下thrift的版本信息
- thrift -version
3. 一个简单的例子
在thrift源代码目录有一个叫tutorial的目录,进行其中后运行thrift命令生成相应的服务代码:
- $ thrift -r --gen cpp tutorial.thrift // -r对其中include的文件也生成服务代码 -gen是生成服务代码的语言
运行完之后会在当前目录看到一个gen-cpp目录,其中就是thrfit命令生成的代码
这时你cd到tutorial/cpp目录,运行make,生成相应的CppServer与CppClient程式。
这时你可以用./CppServer运行服务端,让其监听一个特定的端口
这时你可以用./CppClient运行客户端程式,让其去连接服务端,调用其所对应的服务。默认调用后会输出如下信息:
- lemo@debian:~/Workspace/Facebook/Thrift/thrift-0.5.0/tutorial/cpp$ ./CppServer
- Starting the server...
- ping()
- add(1,1)
- calculate(1,{4,1,0})
- calculate(1,{2,15,10})
- getStruct(1)
如果你的终端中也出现了如上的信息,恭喜你,运行成功了。如果在运行CppServer的时候找不到动态库,看看你是不是运行了make install,如果运行了,再运行一下sudo ldconfig试试。再用ldd CppServer看一下它有没有找到相应的动态库了。
4. 例子分析
4.1 Thrift IDL的分析
这边有两个IDL文件,内容如下:
<strong><span style="font-size:14px;"> shared.thrift
---------------
**
* This Thrift file can be included by other Thrift files that want to share
* these definitions.
*/
namespace cpp shared
namespace java shared
namespace perl shared
// 这里定义了一个结构体,没有定义方法,对应于生成的代码在gen-cpp中的shared_types.h中,其中有一个class叫SharedStruct,
// 有没有看到其中有两个方法叫read和write,这就是用来对其进行序列化与把序列化的方法.
// 对了,其中的i32是Thrift IDL中定义的变量类型,对应于c++语言中的int32_t
struct SharedStruct {
1: i32 key
2: string value
}
// 这里定义的一个服务,它语义上类似于面向对象中的定义一个接口,thrift的编译器会对其产生一套实现其接口的客户端与服务端方法
// 服务的一般定义格式如下
// service <name>
// <returntype> <name>(<arguments>)
// [ throws (<exceptions>)]
// ...
// }
service SharedService {
SharedStruct getStruct(1: i32 key)
}
tutorial.thrift
----------------
/**
* Thrift files can reference other Thrift files to include common struct
* and service definitions. These are found using the current path, or by
* searching relative to any paths specified with the -I compiler flag.
*
* Included objects are accessed using the name of the .thrift file as a
* prefix. i.e. shared.SharedObject
*/
// 这个IDL包含了另一个IDL,也就是说另一个IDL中的对象与服务对其时可见的
include "shared.thrift"
/**
* Thrift files can namespace, package, or prefix their output in various
* target languages.
*/
// 这里定义了一些语言的namespace空间
namespace cpp tutorial
namespace java tutorial
namespace php tutorial
namespace perl tutorial
/**
* Thrift lets you do typedefs to get pretty names for your types. Standard
* C style here.
*/
// 自定义类型
typedef i32 MyInteger
/**
* Thrift also lets you define constants for use across languages. Complex
* types and structs are specified using JSON notation.
*/
// 定义一些变量
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
/**
* You can define enums, which are just 32 bit integers. Values are optional
* and start at 1 if not supplied, C style again.
*/
// 定义枚举类型
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
/**
* Structs are the basic complex data structures. They are comprised of fields
* which each have an integer identifier, a type, a symbolic name, and an
* optional default value.
*
* Fields can be declared "optional", which ensures they will not be included
* in the serialized output if they aren't set. Note that this requires some
* manual management in some languages.
*/
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: optional string comment, //这里的optional字段类型表示如果这个字段的值没有被赋值,它就不会被序列化输出
}
/**
* Structs can also be exceptions, if they are nasty.
*/
// 这里定义了一些异常
exception InvalidOperation {
1: i32 what,
2: string why
}
/**
* Ahh, now onto the cool part, defining a service. Services just need a name
* and can optionally inherit from another service using the extends keyword.
*/
// 这里是定义服务,它继承了shared的服务
service Calculator extends shared.SharedService {
/**
* A method definition looks like C code. It has a return type, arguments,
* and optionally a list of exceptions that it may throw. Note that argument
* lists and exception lists are specified using the exact same syntax as
* field lists in struct or exception definitions.
*/
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
/**
* This method has a oneway modifier. That means the client only makes
* a request and does not listen for any response at all. Oneway methods
* must be void.
*/
oneway void zip()
} </span></strong>
<strong><span style="font-size:14px;"> enum 泛型名
struct 类名</span></strong>
<strong><span style="font-size:14px;"> service 接口名</span></strong>
格式如上所示。
2.1 Types
Thrift类型系统包括预定义基本类型,用户自定义结构体,容器类型,异常和服务定义
(1) 基本类型
binary: 字节数组(byte array)----------------------------------------byte[ ]
bool:布尔类型(true or value),占一个字节------------boolean
byte:有符号字节------------------------------------Byte
i16:16位有符号整型----------------------------------short
i32:32位有符号整型----------------------------------int,Integer
i64:64位有符号整型----------------------------------long,或Date
double:64位浮点数----------------------------------Double
string:未知编码或者二进制的字符串-------------------String
list<t1>: List<t1>
set<t1>: Set<t1>
map<t1,t2>: Map<t1, t2>
注意,thrift不支持无符号整型,因为很多目标语言不存在无符号整型(如java)。
相关例子:
enum TweetType {
TWEET, //a
RETWEET = 2, //b
DM = 0xa, //c
REPLY
} //d
struct Tweet {
1: required i32 userId;
2: required string userName;
3: required string text;
4: optional Location loc;
5: optional TweetType tweetType = TweetType.TWEET // e
16: optional string language = "english"
}
|
说明:
a. 编译器默认从0开始赋值
b. 可以赋予某个常量某个整数
c. 允许常量是十六进制整数
d. 末尾没有逗号
e. 给常量赋缺省值时,使用常量的全称
注意,不同于protocol buffer,thrift不支持枚举类嵌套,枚举常量必须是32位的正整数
2.2 文件包含
Thrift允许thrift文件包含,用户需要使用thrift文件名作为前缀访问被包含的对象,如:
1
2
3
4
5
6
7
8
9
|
include "tweet.thrift" // a
...
struct TweetSearchResult {
1: list<tweet.Tweet> tweets; // b
}
|
说明:
a. thrift文件名要用双引号包含,末尾没有逗号或者分号
b. 注意tweet前缀
2.3 定义结构体
结构体由一系列域组成,每个域有唯一整数标识符,类型,名字和可选的缺省参数组成。如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
struct
Tweet {
1: required i32 userId;
// a
2: required string userName;
// b
3: required string text;
4: optional Location loc;
// c
16: optional string language =
"english"
// d
}
struct
Location {
// e
1: required
double
latitude;
2: required
double
longitude;
}
|
说明:
a. 每个域有一个唯一的,正整数标识符
b. 每个域可以标识为required或者optional(也可以不注明)
c. 结构体可以包含其他结构体
d. 域可以有缺省值
e. 一个thrift中可定义多个结构体,并存在引用关系
规范的struct定义中的每个域均会使用required或者optional关键字进行标识。如果required标识的域没有赋值,thrift将给予提示。如果optional标识的域没有赋值,该域将不会被序列化传输。如果某个optional标识域有缺省值而用户没有重新赋值,则该域的值一直为缺省值。
与service不同,结构体不支持继承,即,一个结构体不能继承另一个结构体。
2.4 定义服务
在流行的序列化/反序列化框架(如protocol buffer)中,thrift是少有的提供多语言间RPC服务的框架。
Thrift编译器会根据选择的目标语言为server产生服务接口代码,为client产生桩代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
//“Twitter”与“{”之间需要有空格!!!
service Twitter {
// 方法定义方式类似于C语言中的方式,它有一个返回值,一系列参数和可选的异常
// 列表. 注意,参数列表和异常列表定义方式与结构体中域定义方式一致.
void
ping(),
// a
bool
postTweet(1:Tweet tweet);
// b
TweetSearchResult searchTweets(1:string query);
// c
// ”oneway”标识符表示client发出请求后不必等待回复(非阻塞)直接进行下面的操作,
// ”oneway”方法的返回值必须是void
oneway
void
zip()
// d
}
|
说明:
a. 函数定义可以使用逗号或者分号标识结束
b. 参数可以是基本类型或者结构体,参数是只读的(const),不可以作为返回值!!!
c. 返回值可以是基本类型或者结构体
d. 返回值可以是void
注意,函数中参数列表的定义方式与struct完全一样
Service支持继承,一个service可使用extends关键字继承另一个service
在cmd下运行:thrift -o ../src/mian/java -out ../src/mian/java --gen java:fullcamel IReward.thrift