视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
关于php使用thrift做服务端开发的那些事
2020-11-02 18:02:20 责编:小采
文档


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 tutorialphp;
    ini_set('display_errors',1);
    error_reporting(E_ALL);
    // 引入类自动加载文件
    require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php';
    // 载入自动加载类
    use ThriftClassLoaderThriftClassLoader;
    // 定义根据.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 ThriftProtocolTBinaryProtocol; // 二进制格式打包解包
    use ThriftTransportTPhpStream; // php流输入输出
    use ThriftTransportTBufferedTransport; // 使用缓存
    // 开始服务端逻辑
    class CalculatorHandler implements 	utorialCalculatorIf {
     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, 	utorialWork $w) {
     error_log("calculate({$logid}, {{$w->op}, {$w->num1}, {$w->num2}})");
     switch ($w->op) {
     case 	utorialOperation::ADD:
     $val = $w->num1 + $w->num2;
     break;
     case 	utorialOperation::SUBTRACT:
     $val = $w->num1 - $w->num2;
     break;
     case 	utorialOperation::MULTIPLY:
     $val = $w->num1 * $w->num2;
     break;
     case 	utorialOperation::DIVIDE:
     if ($w->num2 == 0) {
     $io = new 	utorialInvalidOperation();
     $io->whatOp = $w->op;
     $io->why = "Cannot divide by 0";
     throw $io;
     }
     $val = $w->num1 / $w->num2;
     break;
     default:
     $io = new 	utorialInvalidOperation();
     $io->whatOp = $w->op;
     $io->why = "Invalid Operation";
     throw $io;
     }
     $log = new sharedSharedStruct();
     $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 sharedSharedStruct(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 "
    ";
    }
    $handler = new CalculatorHandler();
    $processor = new 	utorialCalculatorProcessor($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 tutorialphp;
    error_reporting(E_ALL);
    require_once __DIR__.'/../../lib/php/lib/Thrift/ClassLoader/ThriftClassLoader.php';
    use ThriftClassLoaderThriftClassLoader;
    $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 ThriftProtocolTBinaryProtocol;
    use ThriftTransportTSocket;
    use ThriftTransportTHttpClient;
    use ThriftTransportTBufferedTransport;
    use ThriftExceptionTException;
    // 以上配置跟服务端类似
    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 	utorialCalculatorClient($protocol);
     $transport->open();
     $client->ping();
     print "ping()
    ";
     $sum = $client->add(1,1);
     print "1+1=$sum
    ";
     // 调试异常情况
     $work = new 	utorialWork();
     $work->op = 	utorialOperation::DIVIDE;
     $work->num1 = 1;
     $work->num2 = 0;
     try {
     $client->calculate(1, $work);
     print "Whoa! We can divide by zero?
    ";
     } catch (	utorialInvalidOperation $io) {
     print "InvalidOperation: $io->why
    ";
     }
     $work->op = 	utorialOperation::SUBTRACT;
     $work->num1 = 15;
     $work->num2 = 10;
     $diff = $client->calculate(1, $work);
     print "15-10=$diff
    ";
     $log = $client->getStruct(1);
     print "Log: $log->value
    ";
     $transport->close();
    } catch (TException $tx) {
     print 'TException: '.$tx->getMessage()."
    ";
    }

    输出:

    // php client.php --http
    ping()
    1+1=2
    InvalidOperation: Cannot divide by 0
    15-10=5
    Log: PHP is stateless!

    下载本文
    显示全文
    专题