Yar 是一个轻量级, 高效的RPC框架, 它提供了一种简单方法来让PHP项目之间可以互相远程调用对方的本地方法. 并且Yar也提供了并行调用的能力. 可以支持同时调用多个远程服务的方法.
情况: 有个业务场景,需要本地项目去调用一个服务层的相关方法实现相应的功能,一般情况,我可以通过普通的http的方式进行请求即可,但是如果只是这个服务是内部使用,那么可以使用rpc的方式进行替代.好处自不必多说,基于tcp传输,支持并发
实例参考:http://hk2.php.net/manual/zh/yar.examples.php
结合在项目中使用:
首先介绍服务端:RpcServer.php
<?php namespace appindexlogic; /** * 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 appindexlogic; class User extends RpcServer { // 用户扩展信息 public function userExt($ids) { // 1.验证签名 // 2.逻辑处理 // 3.结果返回 return $ids; } // 用户基础信息 public function userBase($ids) { return $ids; } }
客户端:RpcClient.php
<?php namespace appindexlogic; /** * 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 "现在, 所有的请求都发出去了, 还没有任何请求返回 "; } else { echo "这是一个远程调用的返回, 调用的服务名是", $callinfo["method"], ". 调用的sequence是 " , $callinfo["sequence"] , " "; var_dump($retval); } } // 异常回调 public function error_callback($type, $error, $callinfo) { error_log(json_encode(func_get_args() ),3,'rpc.log' ); } }
客户端调用:
<?php namespace appindexlogic; // 相关测试 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调用