您好,登錄后才能下訂單哦!
小編給大家分享一下php使用thrift做服務端開發的案例,希望大家閱讀完這篇文章后大所收獲,下面讓我們一起去探討吧!
php使用thrift做服務端開發
thrift采用接口描述語言定義和創建服務,用二進制格式傳輸數據,體積更小、效率更高,對于高并發、數據量大和多語言的環境有更好的支持。
Apache Thrift是啥?
Apache Thrift是FaceBook開發的一套可擴展的、跨語言的服務調用框架。簡單的說就是先定義一個配置文件,不同的語言可以利用thrift基于這個配置文件生成各自語言的服務端,不管客戶端用什么語言,都可以調用,也就是說基于thrift協議用java可以調用php的服務。目前支持C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi等語言之間相互調用。
相對于傳統的xml和json等數據傳輸方式來說,thrift采用接口描述語言定義和創建服務,用二進制格式傳輸數據,體積更小、效率更高,對于高并發、數據量大和多語言的環境有更好的支持。
thrift安裝環境要求
g++ 4.2
boost 1.53.0
lex and yacc(基于flex和bison)
如果沒安裝lex和yacc的話要先安裝,否則會make失敗,提示lex和yacc command not found錯誤(一般的機器貌似都沒安,Ubuntu用apt-get install flex bision即可)。
安裝thrift
下載最新版thrift:
wget http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.tar.gz tar xvf thrift-0.9.3.tar.gz cd thrift-0.9.3
2.創建configure文件
// 創建./configure文件 ./bootstrap.sh // 配置并安裝 ./configure make // 檢測是否有問題,如果機子沒有安裝python和java等可能會報錯,不過本文主要講php,安了php環境就行 make check make install
編譯選項
使用./configure --help可以查看選項
如果想禁用某個語言,可以用./configure --without-java
thrift for php安裝環境要求
php版本>5.0,因為TBinaryProtocol協議用到了pack()和unpack()函數來序列化數據
需要安裝APC擴展,因為TSocketPool這個類用到了apc_fetch()和apc_store()函數進行apc緩存操作。
php使用thrift的時候,除了要將thrift/lib/php/lib里的基礎文件copy到項目目錄下,還需要將根據配置文件生成的php文件也copy到packages文件夾下,并引入到項目中,這個后續會詳細講。
類庫說明
數據傳輸格式(protocol)
定義的了傳輸內容,對Thrift Type的打包解包,包括:
TBinaryProtocol,二進制格式,TBinaryProtocolAccelerated則是依賴于thrift_protocol擴展的快速打包解包。
TCompactProtocol,壓縮格式
TJSONProtocol,JSON格式
TMultiplexedProtocol,利用前三種數據格式與支持多路復用協議的服務端(同時提供多個服務,TMultiplexedProcessor)交互
數據傳輸方式(transport)
定義了如何發送(write)和接收(read)數據,包括:
TBufferedTransport,緩存傳輸,寫入數據并不立即開始傳輸,直到刷新緩存。
TSocket,使用socket傳輸
TFramedTransport,采用分塊方式進行傳輸,具體傳輸實現依賴其他傳輸方式,比如TSocket
TCurlClient,使用curl與服務端交互
THttpClient,采用stream方式與HTTP服務端交互
TMemoryBuffer,使用內存方式交換數據
TPhpStream,使用PHP標準輸入輸出流進行傳輸
TNullTransport,關閉數據傳輸
TSocketPool在TSocket基礎支持多個服務端管理(需要APC支持),自動剔除無效的服務器
開發流程
1、定義IDL(Interface description language)接口描述文件,后綴.thrift
IDL規范:http://thrift.apache.org/docs/idl
thrift types:http://thrift.apache.org/docs/types
2、服務端代碼開發
3、客戶端編寫接入代碼
IDL:
1.tutorial.thrift
include "shared.thrift" namespace php tutorial typedef i32 MyInteger const i32 INT32CONSTANT = 9853 const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'} enum Operation { ADD = 1, SUBTRACT = 2, MULTIPLY = 3, DIVIDE = 4 } struct Work { 1: i32 num1 = 0, 2: i32 num2, 3: Operation op, 4: optional string comment, } exception InvalidOperation { 1: i32 whatOp, 2: string why } service Calculator extends shared.SharedService { void ping(), i32 add(1:i32 num1, 2:i32 num2), i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), oneway void zip() }
2.shared.thrift
namespace php shared struct SharedStruct { 1: i32 key 2: string value } service SharedService { SharedStruct getStruct(1: i32 key) }
php服務端
<?php namespace tutorial\php; ini_set('display_errors',1); error_reporting(E_ALL); // 引入類自動加載文件 require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php'; // 載入自動加載類 use Thrift\ClassLoader\ThriftClassLoader; // 定義根據.thrift文件生成的php文件 $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; // 注冊thrift服務 $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); $loader->registerDefinition('shared', $GEN_DIR); $loader->registerDefinition('tutorial', $GEN_DIR); $loader->register(); if (php_sapi_name() == 'cli') { ini_set("display_errors", "stderr"); } use Thrift\Protocol\TBinaryProtocol; // 二進制格式打包解包 use Thrift\Transport\TPhpStream; // php流輸入輸出 use Thrift\Transport\TBufferedTransport; // 使用緩存 // 開始服務端邏輯 class CalculatorHandler implements \tutorial\CalculatorIf { protected $log = array(); public function ping() { error_log("ping()"); } // 相加 public function add($num1, $num2) { error_log("add({$num1}, {$num2})"); return $num1 + $num2; } // 枚舉計算類型 public function calculate($logid, \tutorial\Work $w) { error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})"); switch ($w->op) { case \tutorial\Operation::ADD: $val = $w->num1 + $w->num2; break; case \tutorial\Operation::SUBTRACT: $val = $w->num1 - $w->num2; break; case \tutorial\Operation::MULTIPLY: $val = $w->num1 * $w->num2; break; case \tutorial\Operation::DIVIDE: if ($w->num2 == 0) { $io = new \tutorial\InvalidOperation(); $io->whatOp = $w->op; $io->why = "Cannot divide by 0"; throw $io; } $val = $w->num1 / $w->num2; break; default: $io = new \tutorial\InvalidOperation(); $io->whatOp = $w->op; $io->why = "Invalid Operation"; throw $io; } $log = new \shared\SharedStruct(); $log->key = $logid; $log->value = (string)$val; $this->log[$logid] = $log; return $val; } public function getStruct($key) { error_log("getStruct({$key})"); // This actually doesn't work because the PHP interpreter is // restarted for every request. //return $this->log[$key]; return new \shared\SharedStruct(array("key" => $key, "value" => "PHP is stateless!")); } public function zip() { error_log("zip()"); } }; header('Content-Type', 'application/x-thrift'); if (php_sapi_name() == 'cli') { echo "\r\n"; } $handler = new CalculatorHandler(); $processor = new \tutorial\CalculatorProcessor($handler); // 客戶端和服務端在同一個輸入輸出流上 //1) cli 方式:php Client.php | php Server.php //2) cgi 方式:利用Apache或nginx監聽http請求,調用php-fpm處理,將請求轉換為PHP標準輸入輸出流 $transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W)); $protocol = new TBinaryProtocol($transport, true, true); $transport->open(); $processor->process($protocol, $protocol); $transport->close(); //作為cli方式運行,非阻塞方式監聽,基于libevent實現,非官方實現 //$transportFactory = new TBufferedTransportFactory(); //$protocolFactory = new TBinaryProtocolFactory(true, true); //$transport = new TNonblockingServerSocket('localhost', 9090); //$server = new TNonblockingServer($processor, $transport, $transportFactory, $transportFactory, $protocolFactory, $protocolFactory); //$server->serve(); //作為cli方式運行,監聽端口,官方實現 //$transportFactory = new TBufferedTransportFactory(); //$protocolFactory = new TBinaryProtocolFactory(true, true); //$transport = new TServerSocket('localhost', 9090); //$server = new TSimpleServer($processor, $transport, $transportFactory, $transportFactory, $protocolFactory, $protocolFactory); //$server->serve();
php客戶端
<?php namespace tutorial\php; error_reporting(E_ALL); require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php'; use Thrift\ClassLoader\ThriftClassLoader; $GEN_DIR = realpath(dirname(__FILE__).'/..').'/gen-php'; $loader = new ThriftClassLoader(); $loader->registerNamespace('Thrift', __DIR__ . '/../../lib/php/lib'); $loader->registerDefinition('shared', $GEN_DIR); $loader->registerDefinition('tutorial', $GEN_DIR); $loader->register(); use Thrift\Protocol\TBinaryProtocol; use Thrift\Transport\TSocket; use Thrift\Transport\THttpClient; use Thrift\Transport\TBufferedTransport; use Thrift\Exception\TException; // 以上配置跟服務端類似 try { if (array_search('--http', $argv)) { // 使用http方式連接 $socket = new THttpClient('localhost', 8080, '/php/PhpServer.php'); } else { // 使用socket連接 $socket = new TSocket('localhost', 9090); } $transport = new TBufferedTransport($socket, 1024, 1024); $protocol = new TBinaryProtocol($transport); $client = new \tutorial\CalculatorClient($protocol); $transport->open(); $client->ping(); print "ping()\n"; $sum = $client->add(1,1); print "1+1=$sum\n"; // 調試異常情況 $work = new \tutorial\Work(); $work->op = \tutorial\Operation::DIVIDE; $work->num1 = 1; $work->num2 = 0; try { $client->calculate(1, $work); print "Whoa! We can divide by zero?\n"; } catch (\tutorial\InvalidOperation $io) { print "InvalidOperation: $io->why\n"; } $work->op = \tutorial\Operation::SUBTRACT; $work->num1 = 15; $work->num2 = 10; $diff = $client->calculate(1, $work); print "15-10=$diff\n"; $log = $client->getStruct(1); print "Log: $log->value\n"; $transport->close(); } catch (TException $tx) { print 'TException: '.$tx->getMessage()."\n"; }
輸出:
// php client.php --http ping() 1+1=2 InvalidOperation: Cannot divide by 0 15-10=5 Log: PHP is stateless!
看完了這篇文章,相信你對php使用thrift做服務端開發的案例有了一定的了解,想了解更多相關知識,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。