PHP使用Thrift

0x00

Apache Thrift是一个跨语言的服务部署框架,通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(支持C++,Java,Python,PHP, GO,Javascript,Ruby,Erlang,Perl, Haskell, C#等),并由生成的代码负责RPC协议层和传输层的实现。

0x01

现在开始编写一个简单IDL文件helloworld.thrift

namespace php Services.HelloWorld
service HelloWorld
{
    string sayHello(1:string name);
}

然后通过生成器生成PHP文件

#不指明:server不生成processor。。
thrift --gen php:server HelloWorld.thrift

生成文件在gen-php目录下面的Services/HelloWord/HelloWorld.php(目录与namesapce定义一致),这是个公共文件,服务端和客户端都需要包括它。其中客户端调用的代码(HelloWorldClient )已经生成好了
目前的目录结构:

├── gen-php
│   └── Services
│       └── HelloWorld
│           ├── HelloWorld.php
│           └── Types.php
├── helloworld.thrift
//服务端需要继承该接口
interface HelloWorldIf {
  /**
   * @param string $name
   * @return string
   */
  public function sayHello($name);
}
//提供给客户端调用的方法
class HelloWorldClient implements \Services\HelloWorld\HelloWorldIf {
  public function sayHello($name)
  {
    $this->send_sayHello($name);
    return $this->recv_sayHello();
  }
  public function send_sayHello($name)
  {
  }
  public function recv_sayHello()
  {
  }
}
//HelloWord类sayHello方法参数读取
class HelloWorld_sayHello_args {
}
//HelloWord类sayHello方法结果写入
class HelloWorld_sayHello_result {
}
//作为服务端才会生成
class HelloWorldProcessor {
}

0x02

Thrift的PHP类库在Thrift源码中https://github.com/apache/thrift.
可以将thrift/lib/php/lib/Thrift目录复制到当前目录下:

├── gen-php
│   └── Services
├── helloworld.thrift
└── Thrift
    ├── Base
    ├── ClassLoader
    ├── Exception
    ├── Factory
    ├── Protocol
    ├── Serializer
    ├── Server
    ├── StringFunc
    ├── Transport
    └── Type

0x03

服务端的服务实现代码则需要继承HelloWorldIf 实现代码HelloWorldHandler.php


namespace Services\HelloWorld;

class HelloWorldHandler implements HelloWorldIf {
    public function sayHello($name)
    {
        print_r($name.PHP_EOL);
        return "Hello $name";
    }
}

编写服务端代码Server.php


namespace Services\HelloWorld;

error_reporting(E_ALL);

define('THRIFT_ROOT', __DIR__.'/../../../');
require_once  THRIFT_ROOT.'Thrift/ClassLoader/ThriftClassLoader.php';

use Thrift\ClassLoader\ThriftClassLoader;

$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', THRIFT_ROOT);
$loader->registerDefinition('Service', THRIFT_ROOT. 'gen-php');
$loader->register();

use Thrift\Exception\TException;
use Thrift\Factory\TTransportFactory;
use Thrift\Factory\TBinaryProtocolFactory;

use Thrift\Server\TServerSocket;
use Thrift\Server\TSimpleServer;

try {
    require_once 'HelloWorldHandler.php';
    $handler = new \Services\HelloWorld\HelloWorldHandler();
    $processor = new \Services\HelloWorld\HelloWorldProcessor($handler);

    $transportFactory = new TTransportFactory(); 
    $protocolFactory = new TBinaryProtocolFactory(true, true);

    //作为cli方式运行,监听端口,官方实现
    $transport = new TServerSocket('localhost', 9090);
    $server = new TSimpleServer($processor, $transport, $transportFactory, $transportFactory, $protocolFactory, $protocolFactory);
    $server->serve();
} catch (TException $tx) {
    print 'TException: '.$tx->getMessage()."\n";
}

服务端创建的步骤:

  • 首先初始化服务提供者handler
  • 然后利用该handler初始化自动生成的processor
  • 初始化数据传输方式transport
  • 利用该传输方式初始化数据传输格式protocol
  • 开始服务

编写客户端代码Client.php


namespace Services\HelloWorld;

error_reporting(E_ALL);

define('THRIFT_ROOT', __DIR__.'/../../../');
require_once  THRIFT_ROOT.'Thrift/ClassLoader/ThriftClassLoader.php';

use Thrift\ClassLoader\ThriftClassLoader;

$loader = new ThriftClassLoader();
$loader->registerNamespace('Thrift', THRIFT_ROOT);
$loader->registerDefinition('Service', THRIFT_ROOT . 'gen-php');
$loader->register();

use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TSocket;
use Thrift\Transport\TBufferedTransport;
use Thrift\Exception\TException;

try {
    //仅在与服务端处于同一输出输出流有用
    //使用方式:php Client.php | php Server.php 
    //$transport = new TBufferedTransport(new TPhpStream(TPhpStream::MODE_R | TPhpStream::MODE_W));

    //socket方式连接服务端
    //数据传输格式和数据传输方式与服务端一一对应
    //如果服务端以http方式提供服务,可以使用THttpClient/TCurlClient数据传输方式
    $transport = new TBufferedTransport(new TSocket('localhost', 9090));
    $protocol = new TBinaryProtocol($transport);
    $client = new \Services\HelloWorld\HelloWorldClient($protocol);

    $transport->open();

    //同步方式进行交互
    $recv = $client->sayHello('PHP');
    echo "sayHello:".$recv." \n";

    //异步方式进行交互
    $client->send_sayHello('Thrift');
    echo "send_sayHello \n";
    $recv = $client->recv_sayHello();
    echo "recv_sayHello:".$recv." \n";

    $transport->close();
} catch (TException $tx) {
    print 'TException: '.$tx->getMessage()."\n";
}

客户端调用的步骤:

  • 初始化数据传输方式transport,与服务端对应
  • 利用该传输方式初始化数据传输格式protocol,与服务端对应
  • 实例化自动生成的Client对象
  • 开始调用

将以上三个文件放在gen-php/Services/HelloWorld目录下:

gen-php/
└── Services
    └── HelloWorld
        ├── Client.php
        ├── HelloWorldHandler.php
        ├── HelloWorld.php
        ├── Server.php
        └── Types.php

0x04

终端上运行:
先启动Server.php (否则先启动Client.php会报错)

$ cd gen-php/Services/HelloWorld
$ php Server.php

启动Client.php

$ cd gen-php/Services/HelloWorld
$ php Client.php 
sayHello:Hello PHP 
send_sayHello 
recv_sayHello:Hello Thrift 

Server的终端打印:

PHP
Thrift

你可能感兴趣的:(PHP)