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調用