F8 thrift研究
张学 2008-10-14
1. 背景介绍
thrift属于facebook.com技术核心框架之一,使用不同开发语言开发的系统可以通过该框架实现彼此间的通讯,开发者只需编辑一份thrift脚本,即可自动获得其它开发语言的代码(比如 c++、java、 python、ruby、 c#、 haskell、 ocmal、 erlang、 cocoa、 php、 squeak)。
thrift侧重点是构建夸语言的可伸缩的服务,特点就是支持的语言多,同时提供了完整的rpc service framework,可以很方便的直接构建服务,不需要做太多其他的工作。服务端可以根据需要编译成simple | thread-pool | threaded | nonblocking等方式;
thrift支持多种协议格,Thrift的代码实现,有专门的TProtocol和TTransport抽象,相互配合,可以实现多种协议,方便集成各种传输方式,目前支持xml、json等。
使用了epool!
Apache软件基金会已将Thrift作为孵化器项目纳入其中。
thrift目前不支持Windows平台,不过有牛人已经在cygwin上编译调试通过了。
官方开发白皮书:http://incubator.apache.org/thrift/static/thrift-20070401.pdf
2. 研究成果
2.1. 环境与依赖
Ø 操作系统:CentOS 5.2
Ø 依赖:
| Lib | URL |
1 | thrift-20080411p1.tar.gz | http://developers.facebook.com/thrif |
2 | libevent- 1.4.8 -stable.tar.gz | http://monkey.org/~provos/libevent/ |
3 | boost- 1.33.1 | http://www.boost.org/libs/smart_ptr/smart_ptr.htm |
4 | zlib | http://www.zlib.net/ |
2.2. 安装步骤
2.2.1. libEvent安装
1、 下载:http://monkey.org/~provos/libevent/
2、 安装:
>tar –zxvf libevent- 1.4.8 -stable.tar.gz
>cd libevent- 1.4.8 -stable/
>./configure
>make
>make install
2.2.2. boost安装(略)
参考libEvent安装步骤;
2.2.3. zlib安装(略)
参考libEvent安装步骤;
2.2.4. thrift安装
1. 下载:http://developers.facebook.com/thrif
2. 安装:(注:这里忽略了java部分的编译,如果需要请先安装java compiler)
>tar –zxvf thrift-20080411p1.tar.gz
>cd thrift-20080411p1.tar.gz
>./configure CXXFLAGS='-g -O2' --with-java=no
>make
>make install
2.2.5. thrift cpp测试
>cd thrift-20080411p1/test/cpp/
>make –f Makefile.thrift
启动服务:
>./ TestServer
启动选项包括:
[--port=<port number>] 服务端口号;
[--server-type=<server-type>] 服务启动类型;
[--protocol-type=<protocol-type>] 协议类型;
[--workers=<worker-count>] 线程池线程数,仅在服务类型为ThreadPool时有用;
server-type=simple | thread-pool | threaded | nonblocking,默认为simple;
protocol-type= binary | ascii | xml,默认为binary;
启动客户端测试:
>./TestClient
2.2.6. thrift php测试
>cd thrift-20080411p1/test/php/
>make
启动服务:
>./ TestServer
安装php脚本(我是在windows上安装的,步骤如下)
1、 将thrift-20080411p1/lib/php/src/目录下所有文件拷贝到随意目录下的thrift目录下,例如:d:/thriftlib/;
2、 将thrift-20080411p1/test/php/gen-php/目录下的所有文件拷贝到apache主目录下的thrift/gen-php目录下;
3、 在apache配置文件中增加一行:SetEnv THRIFT_ROOT d:/thriftlib/;
4、 重启apache;
5、 修改TestClient.php中的host、port配置,指向Linux服务器上的TestServer;
6、 访问http://localhost/thrift/TestClient.php即可看到测试结果;
2.3. 实战
2.3.1. 目标
实现从指定文本串中提取关键词的webservice,客户端使用php;
接口要求:
输入:字符串(string),返回关键词个数(int32)
输出:关键词列表(list),包含关键词与关键词权重值,发生错误时抛异常(错误号、错误信息)
注:该demo仅作为验证thrift工程用,并非真实实现关键词功能,输出数据为假造数据,另外,关键词lib的初始化、析构等需要自己在产生的server代码中手工编辑;
2.3.2. thrift脚本
创建thrift-20080411p1/keyword目录:
>mkdir keyword
>cd keyword
>vi keyword.thrift
struct SWord{
1: string strWord,
2: double dbQ
}
exception KeywordExceptions {
1: i32 iErrorCode,
2: string strError
}
service CKeyword{
list<SWord> getKeyword(1: string strText, 2: i32 iCount) throws (1: KeywordExceptions e)
}
2.3.3. keyword Server
>mkdir cpp
>cd cpp
>vi Makefile
ifndef thrift_home
thrift_home=../..
endif #thrift_home
target: all
ifndef boost_home
boost_home=/usr/include/boost
endif #boost_home
target: all
include_paths = $(thrift_home)/lib/cpp/src /
$(boost_home)
include_flags = $(patsubst %,-I%, $(include_paths))
# Tools
ifndef THRIFT
THRIFT = ../../compiler/cpp/thrift
endif # THRIFT
CC = g++
LD = g++
# Compiler flags
DCFL = -Wall -O3 -g -I./gen-cpp $(include_flags) -L$(thrift_home)/lib/cpp/.libs -lthrift -lthriftnb -levent
LFL = -L$(thrift_home)/lib/cpp/.libs -lthrift -lthriftnb -levent
CCFL = -Wall -O3 -I./gen-cpp $(include_flags)
CFL = $(CCFL) $(LFL)
all: server
debug: server-debug
stubs: ../keyword.thrift
$(THRIFT) --gen cpp ../keyword.thrift
server-debug: stubs
g++ -o KeywordServer $(DCFL) ./gen-cpp/CKeyword_server.skeleton.cpp ./gen-cpp/CKeyword.cpp ./gen-cpp/keyword_types.cpp ./gen-cpp/keyword_constants.cpp
server: stubs
g++ -o KeywordServer $(CFL) ./gen-cpp/CKeyword_server.skeleton.cpp ./gen-cpp/CKeyword.cpp ./gen-cpp/keyword_types.cpp ./gen-cpp/keyword_constants.cpp
clean:
rm -fr *.o KeywordServer KeywordClient gen-cpp
#生产服务端程序KeywordServer
>make
2.3.4. keyword php
>cd ..
>mkdir php
>cd php
>vi Makefile
# Default target is everything
target: all
# Tools
THRIFT = ../../compiler/cpp/thrift
all: normal inline
normal: stubs
inline: stubs-inline
stubs: ../keyword.thrift
$(THRIFT) --phpl ../keyword.thrift
stubs-inline: ../keyword.thrift
$(THRIFT) --phpi ../keyword.thrift
clean:
$(RM) -r gen-php gen-phpi
#产生php脚本
>make
2.3.5. 测试
>vi TestKeywordClient.php
<?php
if (!isset($GEN_DIR)) {
$GEN_DIR = 'gen-php';
}
$MODE = 'inline';
if (!isset($MODE)) {
$MODE = 'normal';
}
/** Set the Thrift root */
//$GLOBALS['THRIFT_ROOT'] = '../../lib/php/src';
/** Include the Thrift base */
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
/** Include the binary protocol */
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
/** Include the socket layer */
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocketPool.php';
/** Include the socket layer */
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';
echo '==============================='."/n";
echo ' SAFE TO IGNORE THESE IN TEST'."/n";
echo '==============================='."/n";
/** Include the generated code */
require_once $GEN_DIR.'/CKeyword.php';
require_once $GEN_DIR.'/keyword_types.php';
echo '==============================='."/n";
echo ' END OF SAFE ERRORS SECTION'."/n";
echo '==============================='."/n/n";
$host = ' 10.10.10 .99';
$port = 9090;
if ($argc > 1) {
$host = $argv[0];
}
if ($argc > 2) {
$host = $argv[1];
}
$hosts = array($host);
$socket = new TSocket($host, $port);
$socket = new TSocketPool($hosts, $port);
$socket->setDebug(TRUE);
if ($MODE == 'inline') {
$transport = $socket;
$testClient = new CKeywordClient($transport);
} else {
$bufferedSocket = new TBufferedTransport($socket, 1024, 1024);
$transport = $bufferedSocket;
$protocol = new TBinaryProtocol($transport);
$testClient = new ThriftTestClient($protocol);
}
$transport->open();
$start = microtime(true);
/**
* GETKEYWORD TEST
*/
print_r('function getKeyword($strText, $iCount)');
try {
$strText = "这是一个测试关键词获取的字符串!";
$iCount = 8;
$array_keyword = $testClient->getKeyword($strText, $iCount);
print_r('result:');
var_dump($array_keyword);
} catch (KeywordExceptions $e) {
print_r(' caught KeywordExceptions '.$e->iErrorCode.': '.$e->strError."/n");
}
/**
* Normal tests done.
*/
$stop = microtime(true);
$elp = round(1000*($stop - $start), 0);
print_r("Total time: $elp ms/n");
$transport->close();
return;
?>
将./keyword/php/gen-phpi/目录下的所有文件拷贝到apache主目录下的thrift/gen-php目录下,注意,只能用gen-phpi目录下的文件,gen-php目录下的文件测试未通过,目前原因不明,不过可以肯定不是bug,与服务器类型选择有关;
在浏览器中浏览http://localhost/thrift/web/TestKeywordClient.php
测试完毕!
3. 性能测试
以keyword demo为例:
Ø Amd Athlon(tm) 64 X2 Dual Core Processor 4200+ (2CPUs), ~2.2GHz / 2G 内存
Ø Php安装在window 2003 + ph p 5.2.5 + apache2.2.8;
Ø 使用LoadRuner工具测试http://localhost/thrift/web/TestKeywordClient.php的访问并发;
Ø linux为虚拟机(且与windows在一台机器上),该性能仅做参考:
平均并发:87
CPU:100%
该测试并未使用thrift提供的php_thrift_protocol加速扩展模块(仅在linux上可用),该模块可以加速二进制、对象等的传输速度;
4. 存在的缺陷
1、 生成的服务代码编译后,只能是控制台程序,需要自己改造成linux守护进程;
2、 提供的官方文档太简单,很多功能需要读代码;
3、 thrift脚本转cpp的帮助错误;
4、 自动产生的php脚本有bug,Ckeyword.php:: recv_getKeyword函数里面去掉版本判断语句:
if ($ver != 0x80010000) throw new TProtocolException('Bad version identifier: '.$ver, TProtocolException::BAD_VERSION);
5、 未提供官方性能报告;