thrift入门hello示例

引用网上对thrift的介绍:

Thrift源于大名鼎鼎的facebook之手,在2007年facebook提交Apache基金会将Thrift作为一个开源项目,
对于当时的facebook来说创造thrift是为了解决facebook系统中各系统间大数据量的传 输通信以及系统之间语言环境不同需要跨平台的特性。
所以thrift可以支持多种程序语言,例如:  C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk.
在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。
Thrift适用于程序对程 序静态的数据交换,需要先确定好他的数据结构,他是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,
代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,
对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。

主要根据这里的教程:

http://www.thrift.pl/Thrift-tutorial-our-own-hello-world.html

这里面的有几处错误,可能是老版本的thrift。

首先下载安装thrift最新版本。编译安装步骤:

sudo apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
wget -c https://dist.apache.org/repos/dist/release/thrift/0.8.0/thrift-0.8.0.tar.gz
tar -zvxf thrift-0.8.0.tar.gz
cd thrift-0.8.0
./configure
make
sudo make install

安装好后,安装相应的python库,不然在运行python实例的时候会提示“ImportError: No module named Thrift”错误。

cd thrift-0.8.0/lib/py
sudo python setup.py install

ok,现在我们来生成一个thrift文件,定义一个数据类型与方法,在thrift-0.8.0文件夹下面创建hello.thrift文件:

namespace php hello

enum SexType {
    MALE = 1,
    FEMALE = 2
}

struct User {
    1: string firstname,
    2: string lastname,
    3: i32 user_id,
    4: SexType sex,
    5: bool active = false,
    6: optional string description
}

exception InvalidValueException {
    1: i32 error_code,
    2: string error_msg
}

service UserManager {
    void ping(),
    i32 add_user(1:User u) throws (1: InvalidValueException e),
    User get_user(1:i32 uid) throws (1: InvalidValueException e),
    oneway void clear_list()
}

首先我们定义了一个在php代码下的命名空间,namespace php hello。这样在生成相应的php代码后,php类名前加有hello_的前缀。
然后定义了一个性别的枚举类型SexType。
再下面就是结构体User,在定义字段的时候,每个字段前必须加一个序号,后面根数据类型。可以给字段设置默认值,如bool active = false。默认情况下所有字段都为必需,要使其为可选,可加optional修饰符。
exception InvalidValueException 定义了一个异常,当传递的参数不合法时,就抛出此异常。
接下来定义了一个服务类 UserManager,下面是一些方法。格式根我们编程定义的类似,很容易看懂。
最后一个方法加了oneway,表名在客户端调用此方法时,不用等待服务端返回就立马结束。

下面我们用thrift命名来生成相应的代码:

thrift -r --gen php hello.thrift
thrift -r --gen py hello.thrift

这时候,在目录下面出现两个目录,gen-py和gen-php,分别为php与python的代码。

我们用python来写服务端,新建python_server.py:

#!/usr/bin/env python

import sys
sys.path.append('./gen-py')

from hello import UserManager
from hello.ttypes import *

from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer

users = []

class UserManagerHandler:
	def __init__(self):
		pass
		#self.log = {}

	def ping(self):
		print 'ping()'

	def add_user(self, user):
		if user.firstname == None:
			raise InvalidValueException(1,'no firstname exception')
		if user.lastname == None:
			raise InvalidValueException(2, 'no lastname exception')
		if user.user_id <= 0:
			raise InvalidValueException(3, 'wrong user_id')
		if user.sex != SexType.MALE and user.sex != SexType.FEMALE:
			raise InvalidValueException(4, 'wrong sex id')
		print 'Processing user '+user.firstname+' '+user.lastname
		users.append(user)
		print users
		return True

	def get_user(self, user_id):
		if user_id < 0:
			raise InvalidValueException(5, 'wrong id')
		return users[user_id]

	def clear_list(self):
		print 'Clearing list'
		print users
		del users [:]
		print users

handler = UserManagerHandler()
processor = UserManager.Processor(handler)
transport = TSocket.TServerSocket(port=9090)
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()

server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)

# You could do one of these for a multithreaded server
#server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
#server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)

print 'Starting the server...'
server.serve()
print 'done.'

sys.path.append(‘./gen-py’) 这句话是把我们之前用thrift生成的python类代码引用进来。
然后我们运行此服务端:

chmod +x ./python_server.py
./python_server.py

接下来我们用php来实现客户端,当执行php页面后,php去连接python的服务端,调用相应的方法等。

在apache网站根目录下面,我们创建一个文件夹来放示例php代码。

cd ~/www/
mkdir thrift-php
cd thrift-php
cp -r ~/thrift-0.8.0/lib/php/src/ .   #要用到thrift的php类库
cd src
mkdir packages
cd packages
cp -r ~/thrift-0.8.0/gen-php/hello/ .  #将我们之前生成的hello的php部分代码复制过来

然后我们在thrift-php目录下面新建hello.php文件:

<?php
$GLOBALS['THRIFT_ROOT'] = 'src';

require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/THttpClient.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';

require_once $GLOBALS['THRIFT_ROOT'].'/packages/hello/UserManager.php';
try {

	$socket = new TSocket('localhost', 9090);
	$transport = new TBufferedTransport($socket, 1024, 1024);
	$protocol = new TBinaryProtocol($transport);
	$client = new hello_UserManagerClient($protocol);

	$transport->open();
	$client->ping();
	$u = new hello_User();
	$u->user_id = 1;
	$u->firstname = 'John';
	$u->lastname = 'Smith';
	$u->sex = SexType::MALE;
	if ($client->add_user($u))
	{
		echo 'user added succesfully</br>';
	}

	var_dump($client->get_user(0));

	$client->clear_list();

	$u2 = new hello_User();
	$client->add_user($u2);

} catch (hello_InvalidValueException $e) {
	echo $e->error_msg.'<br/>';
}
?>

现在就可以在浏览器中运行我们的php客户端了,打开 http://127.0.0.1/thrift-php/hello.php,显示:

user added succesfully</br>object(hello_User)#7 (6) {
  ["firstname"]=>
  string(4) "John"
  ["lastname"]=>
  string(5) "Smith"
  ["user_id"]=>
  int(1)
  ["sex"]=>
  int(1)
  ["active"]=>
  bool(false)
  ["description"]=>
  NULL
}
no firstname exception

在python的服务器输出:

Starting the server...
ping()
Processing user John Smith
[User(user_id=1, description=None, firstname='John', lastname='Smith', sex=1, active=False)]
Clearing list
[User(user_id=1, description=None, firstname='John', lastname='Smith', sex=1, active=False)]
[]

ok,最基本的hello例子就完成了。

在这里还能找到更加丰富的例子:

https://github.com/apache/thrift/tree/trunk/tutorial