恒久学习【附近的人】---老赵大战Apache Thrift入门篇(九)
大家好,我是老李,你们准备好了吗,我要开始了。
众所周知,老赵是一个爱干活且能干活的人
想当初在积目的时候,服务端四个人都已经懒到家了。老李压根就指挥不动张大彪、柱子,甚至连一向听话的二营长都指挥不动了,最可怕的是老李连老李自己都指挥不动了。事情已然到了这个份上,大家在酒局上一商量,索性找个来砌砖背锅的人得了,要求条件有如下:
爱干活
能干活
一个顶四个
不允许别人抢他的活儿,只允许他抢别人的活儿
眼睛一骨碌,这么一号人物就浮现在眼前:
老赵
今天老赵要来客串一把我们的文章了,他要手把手与Apache Thrift大战三百回合!
首先得说下Apache Thrift是为什么而生。一般百毒一下Thrift的话,官方解释是这样shai儿的:【Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的】。
我跟你说这玩意说的压根就不是人话,说了也不知道啥意思,实际上上面一坨话术表达了Thrift三方面的功能:
数据格式:二进制流、JSON、压缩格式
传输方式:数据在发和收的时候,是怎样的
服务器模型:Thrift为各种语言提供的库中都包含了完整的服务器程序
然后我们再用人话翻译一下Apache Thrift是为什么而生:【Thrift可以用来解决将数据以某种数据格式通过某种传输方式在多种语言之间的服务API中飞来飞去的问题】。
http://mirrors.tuna.tsinghua.edu.cn/apache/thrift/0.12.0/thrift-0.12.0.tar.gz
Thrift整体的使用流程是十分简单的:
用IDL定义好服务接口与返回、接受的数据类型
然后用thrift compiler根据IDL文件生成一坨文件
TIPS:IDL的英文全称叫做Interface Definition Language,这名字你一听就知道是干啥的
所以当前首要的任务是什么?先去厕所给老赵送点儿纸。所以当前首要任务是先定义一个demo级别的IDL文件,比如下面这个:
namespace php Application.User
service UserService {
string login( 1:string username )
}
/* 此部分不要带入到IDL文件中
定义一个接口API,方法名字叫做login,参数是字符串,返回一个字符串
PHP命名空间是:Application\User
*/
上述文件保存成user.thrift,然后用如下命令“编译”一下:
thrift --gen php:server user.thrift
至于你们那里成不成,反正大彪跟我这里成了,gen-php文件夹下,子目录为Application/User,和我们在IDL文件里定义的namespace php Application.User是强相关的。截个图你们感受一下:
这可了不得了!一下子代码全出来了!这家伙要接外包,就只需要定义个IDL然后命令行一生成,不到五分钟钱就骗到手了。好了快醒醒吧,别做梦了,这一坨文件基本都是空壳子而已,它只定了三块主要内容:
方法名称
方法入参参数类型
方法输出结果类型
业务逻辑的砖头,你还是要砌的
其次,我们需要thrift的PHP库文件,在哪儿呢?就在上面下载的thrift源码里,你解压缩后,到根目录的到这个目录里来:lib/php/lib,大彪截个图给他们感受一下:
好了,我们现在要把下面这些东西揉到一块儿了,我们新建一个文件夹叫做php-thrift-server:
thrift的PHP library库,放到php-thrift-server文件夹里来
IDL文件生成好的文件,也就是gen-php里的文件放到php-thrift-server文件夹里来
所以,现在整体目录结构如下图所示:
在php-thrift-server/Application/User文件夹里新建一个文件,比如名字叫做UserServiceHandler.php,这个文件实现了UserServiceIf Interface并实现了我们在IDL文件里定义的login方法的具体业务逻辑,代码如下:
namespace Application\User;
class UserServiceHandler implements UserServiceIf {
public function login($username) {
// 砖头不是你想不砌,不砌就不砌
return "user-login";
}
}
然后开始利用thrift自带的server,快速实现一个php thrift server,在php-thrift-server根目录下创建一个文件叫做server.php,具体代码如下(这段代码请仔细认真分析):
define( "DS", DIRECTORY_SEPARATOR );
define( "ROOT", __DIR__ );
// 自定义一个简单的psr4自动加载器.
spl_autoload_register( function( $s_class_name ) {
$s_class_name = str_replace( "\\", "/", $s_class_name );
$s_file_suffix = '.php';
$s_file_name = $s_class_name.$s_file_suffix;
require_once $s_file_name;
} );
use Thrift\Exception\TException;
use Thrift\Factory\TTransportFactory;
use Thrift\Factory\TBinaryProtocolFactory;
use Thrift\Server\TServerSocket;
use Thrift\Server\TSimpleServer;
use Application\User\UserServiceHandler;
use Application\User\UserServiceProcessor;
try {
$o_handler = new UserServiceHandler();
$o_processor = new UserServiceProcessor( $o_handler );
$o_transport_factory = new TTransportFactory();
$o_protocol_factory = new TBinaryProtocolFactory( true, true );
// 这里 TServerSocket 就官方自带的php cliserver
// 但我告诉你,这个server生产环境没法用...demo一下尚可
$o_transport = new TServerSocket( 'localhost', 9090 );
$o_server = new TSimpleServer( $o_processor, $o_transport, $o_transport_factory, $o_transport_factory, $o_protocol_factory, $o_protocol_factory );
echo "php-thrift-server start".PHP_EOL;
$o_server->serve();
} catch ( TException $tx ) {
print 'TException: '.$tx->getMessage()."\n";
}
5-11行,我搞了一个巨粗暴的PSR4的autoload,反正能用
23行,分别初始化服务handler和服务processor,这里你可以先不必理解这是啥玩意
25、24行是数据传输方式工厂方法和数据协议工厂方法。比如数据传输方式工厂会创建是通过socket传输或者curl或者stream,而数据传输协议工厂方法则会创建数据协议(二进制、JSON、压缩)
28行表示要在localhost的9999端口创建一个服务器
29行表示在38行创建的服务器基础上,利用35、36行选择好的数据传输格式和数据传输方式进行服务
31行,不用我说了
上面代码你复制粘贴走,然后命令行下php server.php执行一把:
。。。。。。反正看起来服务端好像没啥问题了。。。下面得复制粘贴一下客户端client.php的代码了,这里有一点要注意的是:
客户端的数据传输方式和数据传输格式一定要和服务端保持一致
define( "DS", DIRECTORY_SEPARATOR );
define( "ROOT", __DIR__ );
// 自定义一个简单的psr4自动加载器.
spl_autoload_register( function( $s_class_name ) {
$s_class_name = str_replace( "\\", "/", $s_class_name );
$s_file_suffix = '.php';
$s_file_name = $s_class_name.$s_file_suffix;
require_once $s_file_name;
} );
use Thrift\Exception\TException;
use Thrift\Transport\TSocket;
use Thrift\Transport\TBufferedTransport;
use Thrift\Protocol\TBinaryProtocol;
use Application\User\UserServiceClient;
try {
$transport = new TBufferedTransport( new TSocket( 'localhost', 9090 ) );
$protocol = new TBinaryProtocol( $transport );
$client = new UserServiceClient( $protocol );
$transport->open();
//同步方式进行交互
$recv = $client->login('Courages');
echo "\n 收到服务器返回:".$recv." \n";
$transport->close();
} catch ( TException $tx ) {
print 'TException: '.$tx->getMessage()."\n";
}
上面客户端的代码中已经基本能说明数据传输协议采用TBinaryProtocol(二进制),数据传输方式采用(TSocket)。上面代码复制粘贴保存一下,然后php client.php执行一下:
好了,到这里我们得继续聊两个问题:
一来是为什么不用JSON在各种语言的api中飞来飞去
二来是我们上面的php thrift是搭配着thrift php库的server代码运行起来的,但在实际应用里尤其是生产环境里是无法这么直接用。因为一般使用时候,要么是fpm运行环境,要么是swoole或者workerman
第一个问题,JSON是文本格式,相对于这个来说Thrift生成的二进制数据体积更小,这样网络消耗会更小。其次是序列化和反序列化的速度相比,thrift理论上会更好一些。而且,你们一定要记住:
apache thrift是一套完整的跨语言PRC解决方案,而JSON只是JSON
第二个问题,我们暂且放到第二篇章,文章写到这里有点儿刹不住车,再下去就有点儿臭长臭长了。