php擴展之Yar


  Yar 是一個輕量級, 高效的RPC框架, 它提供了一種簡單方法來讓PHP項目之間可以互相遠程調用對方的本地方法. 並且Yar也提供了並行調用的能力. 可以支持同時調用多個遠程服務的方法.

 

  情況: 有個業務場景,需要本地項目去調用一個服務層的相關方法實現相應的功能,一般情況,我可以通過普通的http的方式進行請求即可,但是如果只是這個服務是內部使用,那么可以使用rpc的方式進行替代.好處自不必多說,基於tcp傳輸,支持並發

  實例參考:http://hk2.php.net/manual/zh/yar.examples.php

  結合在項目中使用:

    首先介紹服務端:RpcServer.php

<?php 
namespace app\index\logic;
/**
 * rpc基類(服務端)
 */
class RpcServer
{
    private static $signs = [
        'sign1', //不同的來源Salt不同
        'sign2',
    ];

    // 驗證簽名
    protected function checkSign($params,$sign)
    {
        if(empty($sign)){
            return false;
        }

        ksort($params);
        $signStr = '';
        foreach($params as $key => $val)
        {
            if(empty($val) || $val == $sign) continue;
            $signStr .= $key.'='.$val.'&';
        }

        $signStr = rtrim($signStr,'&');
        foreach (self::$signs as $v){
            if(md5($signStr.$v) === $sign){
                return true;
            }
        }

    }

    // 處理結果
    protected function response($status,$data)
    {
        $response = [
            'status'    =>    $status,
            'message'    =>    '', //狀態碼對應的信息(從配置文件中獲取)
            'data'        =>    $data,
        ];

        return $response;
    }
}

  服務端方法:

    

<?php  
namespace app\index\logic;

class User extends RpcServer
{

    // 用戶擴展信息
    public function userExt($ids)
    {
        // 1.驗證簽名
        // 2.邏輯處理
        // 3.結果返回
        return $ids;
    }

    // 用戶基礎信息
    public function userBase($ids)
    {
        return $ids;
    }


}

 

  客戶端:RpcClient.php

<?php
namespace app\index\logic;

/**
 * rpc基類(客戶端)
 */
class RpcClient
{

    private static $signs = [
        'sign1', //不同來源
        'sign2'
    ];

    private $callBack;
    private $callNum = 0;


    /**
     * 取得簽名
     * @param  $params 接口調用時的參數
     */
    protected function getSign($params,$type)
    {
        ksort($params);
        $signStr = '';
        foreach($params as $key => $val)
        {
            if(empty($val)) continue;
            $signStr .= $key.'='.$val.'&';
        }
        $signStr = rtrim($signStr,'&');
        return md5($signStr.self::$signs[$type]);
    }

   /**
     * 調用服務端接口
     * @param  $server      Api server
     * @param  $api         接口
     * @param  $params      參數
     * @param  $openSign    開啟簽名
     * @param  $callBack    回調
     */ 
    public function call($server,$api,$params,$openSign=false,$callBack=null)
    {
        if($openSign){
            $params['sign'] = $this->getSign($params);
        }
 
        if($callBack === null){
            $client = new \Yar_Client($server);
            return call_user_func_array([$client,$api], $params);
        }
        $this->callNum ++;
        $this->callBack = $callBack;
        return \Yar_Concurrent_Client::call($server,$api,$params,array($this, 'ApiClientCallBack'));
    }

     /**
     * 執行並發調用
     */
    public function loop()
    {
        return \Yar_Concurrent_Client::loop([$this,'callback1'],[$this,'error_callback']); 
    }

     /**
     * 並發調用回調
     * @param  $retval
     * @param  $callinfo
     */
    public function ApiClientCallBack($retval,$callinfo)
    {    
        if($callinfo === null){
            return $this->callBack($retval,$callinfo);
        }

        static $data = array();
        $data[] = $retval; //並發
        if(count($data) == $this->callNum){
            $fn = $this->callBack;
            return $this->$fn($data,$callinfo);
        }
    }

    // 
    public function callback1($retval, $callinfo)
    {
             if ($callinfo == NULL) {
                echo "現在, 所有的請求都發出去了, 還沒有任何請求返回\n";
             } else {
                echo "這是一個遠程調用的返回, 調用的服務名是", $callinfo["method"], 
                              ". 調用的sequence是 " , $callinfo["sequence"] , "\n";
                var_dump($retval);
             }
    }

    // 異常回調
    public function error_callback($type, $error, $callinfo)
    {
        error_log(json_encode(func_get_args() ),3,'rpc.log' );
    }

}

  客戶端調用:

<?php 
namespace app\index\logic;

// 相關測試
class Test extends RpcClient
{
    public function testRpc()
    {

        $api = 'http://thinkphp.com/index/rpc/users';

        // $this->call($api,'userExt',[1,2],false,'callback');
        $this->call($api,'userBase',[3,4],false,'callback');
        $this->call($api,'userBase',[5],false,'callback');
        $this->loop();

        return false;
        // $client = new yar_client("http://thinkphp.com/index/rpc/user");
        // $ret = $client->userInfo([1,2]);
        // var_dump($ret);
    }

    // 回調數據
    public function callback($data,$callinfo)
    {
        var_dump(func_get_args());die;
        // static $a = [];
        // $a[] = json_encode(func_get_args());
        // print_r($a);
        // error_log(json_encode(func_get_args() ),3,'rpc.log' );
    }
}

 

  即可實現簡單的rpc調用


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM