• 技术文章 >后端开发 >php教程

    关于php使用thrift做服务端开发的那些事

    藏色散人藏色散人2020-10-12 14:26:01转载749
    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安装环境要求

    如果没安装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

    编译选项

    thrift for php安装环境要求

    php使用thrift的时候,除了要将thrift/lib/php/lib里的基础文件copy到项目目录下,还需要将根据配置文件生成的php文件也copy到packages文件夹下,并引入到项目中,这个后续会详细讲。

    类库说明

    数据传输格式(protocol)

    定义的了传输内容,对Thrift Type的打包解包,包括:

    数据传输方式(transport)

    定义了如何发送(write)和接收(read)数据,包括:

    开发流程

    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做服务端开发的那些事的详细内容,更多请关注php中文网其它相关文章!

    本文转载于:csdn,如有侵犯,请联系a@php.cn删除
    专题推荐:thrift php
    上一篇:分享几种用PHP写99乘法表的方式 下一篇:怎么使PHP服务器在有限的资源里最大提升并发能力
    第13期线上培训班

    相关文章推荐

    • 详解Python中Thrift的示例代码分享• Thrift框架快速入门方法• 如何处理Mac安装thrift因bison报错• php+nodeJs+thrift协议,实现zookeeper节点数据自动发现

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网