您的位置:首页 > 其它

Thrift相关知识总结

2014-10-14 18:41 225 查看

1 Thrift简介

目前流行的服务调用方式有很多种,例如基于 SOAP 消息格式的 Web Service,基于 JSON 消息格式的 RESTful 服务等。其中所用到的数据传输方式包括 XML,JSON 等,然而 XML 相对体积太大,传输效率低,JSON 体积较小,新颖,但还不够完善。本文将介绍由
Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa,Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势。

2 Thrift架构

Thrift 包含一个完整的堆栈结构用于构建客户端和服务器端。下图描绘了 Thrift 的整体架构。



图 1. 架构图

如图所示,图中黄色部分是用户实现的业务逻辑,褐色部分是根据 Thrift 定义的服务接口描述文件生成的客户端和服务器端代码框架,红色部分是根据Thrift 文件生成代码实现数据的读写操作。红色部分以下是 Thrift 的传输体系、协议以及底层 I/O 通信,使用 Thrift 可以很方便的定义一个服务并且选择不同的传输协议和传输层而不用重新生成代码。

Thrift 服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器 / 容器一起运行,可以和现有的 J2EE 服务器 /Web 容器无缝的结合。

3 Thrift安装

在一个干净的系统上,很快就能完成。

Thrift至少会依赖于两个系统中一般不会带的库:libevent,boost。 http://thrift.apache.org/download/
libevent到这里下载:http://monkey.org/~provos/libevent/

boost 到这里下载:http://www.boost.org/

安装libevent:

./configure

make

makeinstall

安装boost(boost不像一般的Linux源码安装包一样,它的安装不是configure,make,makeinstall,有点怪):

./bootstrap.sh

./b2

./b2install

安装Thrift:

./configure

make

makeinstall



4 Thrift文件的编写

1.thrift的数据类型
a. 基本数据类型

1) boolean型,值为true或false
2) byte型,值为单字节字母
3) i16型,值长度为16位的integer带符号数字.
4) i32型,值长度为32位的integer带符号数字.
5) i64型,值长度为64位的integer带符号数字.
6) double型,值长度为64的浮点数.
7) string型,字符串或者binary数据。
8) binary型,为java提供

 b. 结构体:
 有点类似c的结构体。
 型如:
struct Work {
1: i32 num1 =0,
2: i32 num2,
3: Operation op,
4: optionalstring comment,
}
c. 容器

1) list类型。
指一组有序元素的集合,类似于java的ArrayList类。
typedefi32 MyInteger
MyInteger a;
struct hello{
list<MyInteger> d;
}
指hello结构体中含有一个list类型的数据,且里面的元素都必须是a数据类型的MyInteger数据类型的数据,主要这里的typedef起一个别名的作用。
2) set类型
一个不可重复的排序元素的集合,类似java的hashset,python的set.使用同list
3) map<type1,type2>类型。
 
使用如:
map<string,string> MAPCONSTANT = {'hello':'world','goodnight':'moon'}

2. 服务
  有了数据结构,那么就该对数据进行操作。thrift这里所说的服务,不可能针对我们自己的业务逻辑,而是指的是服务端从端口读取数据,客户端传入数据的一套方法。

service <服务名>{
<返回类型> <方法名>(参数) throws <异常>
}
根据前面所说数据类型和结构体,编写如下thrift文件,名为 test.thrift

const i32 a = 1
const i32 b = 2
const stringc ="helloworld"
const list<string>d ="test"
const map<string,string> e = {"hello":"world","dd":"ee"}

struct f{
1:i32 a,
2:i32 b,
3:string c,
4:list<string>d=["ceshi"],
5:map<string,string> e = {"hello":"world"},
}
exception Exception{
1:i32 what;
2:stringwhere;
}
service Hello{
fget_f(1:f gg) throws (1:Exception e)
}
3. 实践

i32 a;
i32 b;
string c;
list<string> d;
map<string,string> e = {"hello": "world", "dd": "ee"};

struct f{
1:a,
2:b,
3:c=2,
4:d="ceshi",
5:e,
}

service Hello{
fget_f(1:f gg) throws (Exception, e)
}

以上代码使用thrift的标准命令(thrift.exe -gen py test.thrift)是不成功,因为语法还是不正确!

修改

1.常量量如a,b,c,d, e要使用const修饰。

2. 如果都使用了const修饰,但有;号,还是不成功,尽管有的人说thrift对;不敏感,那还是估计看版本的,这里我使用的是最新的0.8版本。也就是说,如果你是一个java或者php程序员,请注意没有";"

3. 静态变量请赋值,如consti32 a = "helloworld"

4. 结构体内部的属性请指定参数类型,因为结构体内部属性和外部的静态属性没有任何关系。

5. 异常之间没有“,”分割,以空格代替

6. Exception类型请定义。

7. throws参数也要指定是第几个参数

好了,修改后的thrift脚本变为

5 编译thriift文件生成代码

Windows:

thrift-0.7.0.exe -r -gen java**.thrift //生成java 代码

thrift-0.7.0.exe -r -gen php**.thrift //生成php代码

thrift-0.7.0.exe -r -gen py**.thrift //生成python代码

thrift-0.7.0.exe -r -gen as3 **.thrift //生成as3代码

thrift-0.7.0.exe -r -gen cpp**.thrift //生成C++代码

Linux:

thrift -r -genjava **.thrift //生成java 代码

thrift -r -genphp **.thrift //生成php代码

thrift -r -genpy **.thrift //生成python代码

thrift -r -genas3 **.thrift //生成as3代码

thrift -r -gencpp **.thrift //生成C++代码

6 Thrift服务器与客户端通信样例

6.1 客户端JAVA,服务端JAVA

创建一个简单的服务
Hello。首先根据 Thrift 的语法规范编写脚本文件 Hello.thrift,代码如下:

清单1:Hello.thrift

namespace java service.demo
service Hello{
string helloString(1:string para)
i32 helloInt(1:i32 para)
bool helloBoolean(1:bool para)
void helloVoid()
string helloNull()
}
其中定义了服务 Hello 的五个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。 Thrift 是对 IDL(Interface DefinitionLanguage) 描述性语言的一种具体实现。因此,以上的服务描述文件使用
IDL 语法编写。使用 Thrift 工具编译 Hello.thrift,就会生成相应的 Hello.java 文件。该文件包含了在 Hello.thrift 文件中描述的服务 Hello 的接口定义,即 Hello.Iface 接口,以及服务调用的底层通信细节,包括客户端的调用逻辑 Hello.Client 以及服务器端的处理逻辑 Hello.Processor,用于构建客户端和服务器端的功能。

创建HelloServiceImpl.java 文件并实现 Hello.java 文件中的 Hello.Iface 接口,代码如下:

清单2:HelloServiceImpl.java
package service.demo;
import org.apache.thrift.TException;
public class HelloServiceImpl implementsHello.Iface {
@Override
public boolean helloBoolean(boolean para)throws TException {
return para;
}
@Override
public int helloInt(int para) throwsTException {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return para;
}
@Override
public String helloNull() throws TException{
return null;
}
@Override
public String helloString(String para)throws TException {
return para;
}
@Override
public void helloVoid() throws TException {
System.out.println("HelloWorld");
}
}

创建服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给 Thrift 服务器,代码如下:

清单3:HelloServiceServer.java
package service.server;
import org.apache.thrift.TProcessor;
importorg.apache.thrift.protocol.TBinaryProtocol;
importorg.apache.thrift.protocol.TBinaryProtocol.Factory;
import org.apache.thrift.server.TServer;
importorg.apache.thrift.server.TThreadPoolServer;
importorg.apache.thrift.transport.TServerSocket;
importorg.apache.thrift.transport.TTransportException;
import service.demo.Hello;
import service.demo.HelloServiceImpl;

public class HelloServiceServer {

/**
* 启动 Thrift 服务器
* @param args
*/
public static void main(String[] args) {
try {
// 设置服务端口为 7911
TServerSocket serverTransport = newTServerSocket(7911);
// 设置协议工厂为TBinaryProtocol.Factory
Factory proFactory = newTBinaryProtocol.Factory();
// 关联处理器与 Hello 服务的实现
TProcessor processor = newHello.Processor(new HelloServiceImpl());
TServer server = newTThreadPoolServer(processor, serverTransport,
proFactory);

System.out.println("Startserver on port 7911...");
server.serve();
} catch (TTransportException e) {
e.printStackTrace();

}
}
}

创建客户端实现代码,调用 Hello.client 访问服务端的逻辑实现,代码如下:

清单4:[b] HelloServiceClient.java[/b]
package service.client;
import org.apache.thrift.TException;
importorg.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
importorg.apache.thrift.transport.TTransportException;
import service.demo.Hello;

public class HelloServiceClient {
/**
* 调用 Hello 服务
* @param args
*/
public static void main(String[] args) {
try {
// 设置调用的服务地址为本地,端口为7911
TTransport transport = newTSocket("localhost", 7911);
transport.open();
// 设置传输协议为TBinaryProtocol
TProtocol protocol = newTBinaryProtocol(transport);
Hello.Client client = newHello.Client(protocol);
// 调用服务的 helloVoid 方法
client.helloVoid();
transport.close();
} catch (TTransportException e) {
e.printStackTrace();
} catch (TException e) {
e.printStackTrace();
}
}
}

代码编写完后运行服务器,再启动客户端调用服务 Hello 的方法 helloVoid,在服务器端的控制台窗口输出“Hello World”(helloVoid 方法实现在控制台打印字符串,没有返回值,所以客户端调用方法后没有返回值输出,读者可以自己尝试其他有返回值方法的调用,其结果可以打印在客户端的控制台窗口
)。

6.2 客户端C++,服务端JAVA

清单1:[b]Hello.thrift[/b]


namespace java service.demo

service Hello{
string helloString(1:string para)

i32 helloInt(1:i32 para)
bool helloBoolean(1:bool para)

void helloVoid()
string helloNull()
}

其中定义了服务 Hello 的五个方法,每个方法包含一个方法名,参数列表和返回类型。每个参数包括参数序号,参数类型以及参数名。 Thrift 是对 IDL(Interface
DefinitionLanguage) 描述性语言的一种具体实现。因此,以上的服务描述文件使用 IDL 语法编写。使用 Thrift 工具以-cpp参数编译Hello.thrift,就会生成相应的 *.c和*.h 文件。使用 Thrift 工具以-java参数编译Hello.thrift,就会生成相应的.java文件。其中服务器端java代码同上,在此不再列出

清单2:[b]Hello_client.cpp[/b]

#include "Hello.h" // Asan example

#include <transport/TSocket.h>
#include <transport/TBufferTransports.h>
#include <protocol/TBinaryProtocol.h>
using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

int main(int argc, char **argv) {
boost::shared_ptr<TSocket>socket(new TSocket("localhost", 7911));//注意端口号的匹配
boost::shared_ptr<TTransport> transport(newTBufferedTransport(socket));
boost::shared_ptr<TProtocol>protocol(new TBinaryProtocol(transport));
HelloClient client(protocol);
transport->open();
client.helloVoid();
transport->close();
return 0;
}
清单3: makefile

BOOST_DIR= /usr/local/include/boost/

THRIFT_DIR= /usr/local/include/thrift/

LIB_DIR= /usr/local/lib/

GEN_SRC:= Hello.cpp Hello_constants.cpp Hello_types.cpp

default:hello_client

hello_client:Hello_client.cpp

g++ -DHAVE_NETINET_IN_H -o hello_client-I${THRIFT_DIR} -I${BOOST_DIR} -I./ -L${LIB_DIR} -lthrift Hello_client.cpp${GEN_SRC}

clean:

$(RM) -r hello_client

将通过-cpp参数生成的.cpp和.h文件同Hello_client.cpp和makefile
文件放到同一目录下,删除Hello_server.skeleton.cpp文件,然后执行make命令进行编译。

执行./hello_client
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: