Thrift RPC 通訊搭建

994914376發表於2019-01-03

一、先安裝 Thrift 編譯工具

MAC 電腦安裝執行以下命令

    brew install thrift

Linux 環境下安裝參考官網

官網地址:http://thrift.apache.org/download

安裝完成之後

    $ thrift --version
    Thrift version 0.10.0

有以上輸出說明工具安裝成功

二、定義自己的Thrift 返回引數結構體

我這裡定義了一個 thriftCommon.thrift 檔案 這裡我以 lumen框架為例 存放目錄為thriftSource/thriftCommon.thrift 內容如下:

namespace php app.Library.Thrift  # 指定生成什麼語言,生成檔案存放的目錄

// 返回結構體
struct Response {
    1: i32 code;    // 返回狀態碼
    2: string msg;  // 碼字回提示語名
    3: string data; // 返回內容
}

// 服務體
service ThriftCommonCallService {
    // json 字串引數  客戶端請求方法
    Response invokeMethod(1:string params)
}

定義好以上檔案之後使用第一步安裝好的 thrift 工具來生成相應的檔案

$ thrift -r --out ./ --gen php thriftSource/thriftCommon.thrift

執行該命令會在app/Library/Thrift目錄生成ThriftCommonCallService.phpTypes.php檔案,這是客戶端檔案可以分兩個專案部署,也可以在同一個專案部署,我這邊因為工作需要獨立部署。

三、建立一個新的客戶端專案

執行以下建立命令

    $ composer create-project laravel/lumen server --prefer-dist "5.5"

把第二步生成的客戶端的兩個檔案拷貝到 app/Library/Thrift目錄 如圖:

file

四、服務端的實現

安裝第三方使用包

$ composer require apache/thrift

使用 thrift 命令生成服務端程式碼

    $ thrift -r --out ./ --gen php:server thriftSource/thriftCommon.thrift

執行該命令會在app/Library/Thrift目錄生成ThriftCommonCallService.phpTypes.php檔案 開啟 ThriftCommonCallService.php檔案我們需要實現裡面的介面ThriftCommonCallServiceIf 接下來我們建立實現檔案 app/Library/Rpc/Server.php

<?php
namespace App\Library\Rpc;

use App\Library\Thrift\ThriftCommonCallServiceIf;
use App\Library\Thrift\Response;

class Server implements ThriftCommonCallServiceIf
{
    /**
     * 實現 socket 各個service 之間的轉發
     * @param string $params
     * @return Response
     * @throws \Exception
     */
    public function invokeMethod($params)
    {
         // 轉換字串 json
         $input = json_decode($params, true);
         // 自己可以實現轉發的業務邏輯
        ...

        $response = new Response();
        $response->code = 200;
        $response->msg = '';
        $response->data = json_encode($input);
        return $response;
    }

建立控制器實現服務端 埠啟動 app/Http/Controllers/SocketController.php

<?php
namespace App\Http\Controllers;

use App\Library\Rpc\Server;
use app\Library\Thrift\Response;
use app\Library\Thrift\ThriftCommonCallServiceProcessor;
use Illuminate\Http\Request;
use Thrift\Exception\TException;
use Thrift\Factory\TBinaryProtocolFactory;
use Thrift\Factory\TTransportFactory;
use Thrift\Server\TServerSocket;
use Thrift\Server\TSimpleServer;
use Thrift\TMultiplexedProcessor;

class SocketController extends Controller
{
    /**
     * 啟動 socket 連線
     */
    public function server()
    {
        try {
            $thriftProcessor = new ThriftCommonCallServiceProcessor(new Server());
            $tFactory = new TTransportFactory();
            $pFactory = new TBinaryProtocolFactory(true, true);
            $processor = new TMultiplexedProcessor();
            // 註冊服務
            $processor->registerProcessor('thriftCommonCallService', $thriftProcessor);

            // 監聽開始
            $transport = new TServerSocket('127.0.0.1', 9999);
            $server = new TSimpleServer($processor, $transport, $tFactory, $tFactory, $pFactory, $pFactory);
            $server->serve();
        } catch (TException $te) {
            app('log')->error('socket 服務啟動失敗', ['content' => $te->getMessage()]);
        }
    }

建立訪問路由或php artisan啟動命令

// 訪問路由
$router->get('/rpc/server', 'SocketController@server');

建立php artisan檔案 目錄 app/Console/Commands/RpcServer.php

 <?php
/**
 * Created by PhpStorm.
 */

namespace App\Console\Commands;

use App\Http\Controllers\SocketController;
use Illuminate\Console\Command;

class RpcServer extends Command
{
    /**
     * 控制檯命令 signature 的名稱。
     *
     * @var string
     */
    protected $signature = 'server:rpc';

    /**
     * 控制檯命令說明。
     *
     * @var string
     */
    protected $description = 'rpc 服務';

    protected static $socketController;

    /**
     * rpcServer constructor.
     * @param SocketController $socketController
     */
    public function __construct(SocketController $socketController)
    {
        parent::__construct();
        self::$socketController = $socketController;
    }

    /**
     * 執行控制檯命令。
     *
     * @return mixed
     */
    public function handle()
    {
        self::$socketController->server();
    }
}

app/Console/Kernel.php註冊

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Laravel\Lumen\Console\Kernel as ConsoleKernel;
use App\Console\Commands\RpcServer;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        RpcServer::class
    ];

到此服務端實現已全部完成 接下來實現客戶端併成功與服務端通訊

五、客戶端的實現 (以第三步建立的專案為例繼續說明)

安裝第三方使用包

$ composer require apache/thrift

建立檔案 app/Library/Tools/Socket.php

<?php
namespace App\Library\Tools;

use app\Library\Thrift\ThriftCommonCallServiceClient;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Protocol\TMultiplexedProtocol;
use Thrift\Transport\TBufferedTransport;
use Thrift\Transport\TSocket;

class Socket
{
    // 儲存物件例項化
    private $_instance;

    // 儲存服務連線池
    private static $client = [];

    // 配置檔案
    private $config = [];

    private function __construct($type)
    {
        $config = [
            'erp' => [
                'host' => env('ERP_RPC_HOST'),
                'port' => env('ERP_RPC_PORT')
            ]
        ];
        $this->config = $config[$type];
    }

    /**
     * 連線服務
     * @param string $name  呼叫的方法名
     * @param array $args   1、引數陣列 2、具體哪個方法名  3、所屬的 Service 名稱
     * @return bool
     */
    public static function __callStatic($name, $args)
    {
        if (substr($name, 0, 8) != 'SocketTo') {
            return false;
        }

        $type = strtolower(substr($name, 8));
        // 例項化操作
        if (!isset(self::$client[$type])) {
            self::$client[$type] = new self($type);
        }

        return self::$client[$type]->invoke($args);
    }

    private function invoke($args)
    {
        try {
            $socket = new TSocket($this->config['host'], $this->config['port']);
            $socket->setRecvTimeout(50000);
            $socket->setDebug(true);
            $transport = new TBufferedTransport($socket, 1024, 1024);
            $protocol = new TBinaryProtocol($transport);
            $thriftProtocol = new TMultiplexedProtocol($protocol, 'thriftCommonCallService');
            $client = new ThriftCommonCallServiceClient($thriftProtocol);
            $transport->open();
            // 拼裝引數與型別
            $data = [
                'params' => $args[0],
                'methodName' => $args[1],
                'serviceName' => $args[2]
            ];
            $result = $client->invokeMethod(json_encode($data));
            $result->data = json_decode($result->data, true);
            $transport->close();
            return $result;
        } catch (\TException $Te) {
            app('log')->error('服務連線失敗 ', ['host' => $this->config, 'methodName' => $args[1], 'content' => $Te->getMessage()]);
            return ['host' => $this->config, 'methodName' => $args[1], 'content' => $Te->getMessage()];
        }
    }
}

建立控制器來呼叫 並配置路由方問

<?php
namespace App\Http\Controllers;

use App\Library\Tools\Socket;
use Illuminate\Http\Request;

class ClientController extends Controller
{
    public function client(Request $request)
    {
       return ['code' => 20000, 'data' => ['name' => 'admin', 'roles' => ['admin']]];
       //接收引數
       $param = $request->input("params");
       //呼叫Service
        $data = Socket::SocketToErp($param, 'login', 'LoginService');
        return response()->json($data);
    }

以上就是實現客戶端與服務端通訊的內容,如有不懂可留言一塊討論^_^

相關文章