php编译成二进制文件_PHP使用thrift做服务端开发

本文介绍如何使用Thrift进行跨语言的服务开发,重点讲解PHP环境下Thrift的安装配置及示例代码,涵盖服务端与客户端的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

02fc4a81421889650cbc8bbd27237355.png

php中文网最新课程

每日17点准时技术干货分享

39a476f29e0ac6db1e3ea4269b1219e3.png

0e45681cc6273727c73a12c640395ee4.gif

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

1.下载最新版thrift:

wget http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.3/thrift-0.9.3.tar.gztar xvf thrift-0.9.3.tar.gzcd thrift-0.9.3

2.创建configure文件

// 创建./configure文件./bootstrap.sh// 配置并安装./configuremake// 检测是否有问题,如果机子没有安装python和java等可能会报错,不过本文主要讲php,安了php环境就行make checkmake 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 tutorialtypedef i32 MyIntegerconst i32 INT32CONSTANT = 9853const map 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 sharedstruct 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 --httpping()1+1=2InvalidOperation: Cannot divide by 015-10=5Log: PHP is stateless!

dd20b655253500118bcb93934614ddcb.png

6a1fa9b96093b96106426dd0b5b4bbdd.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值