php扩展之Yar_phpYar 是一个轻量级, 高效的RPC框架, 它提供了一种简单方法来让PHP项目之间可以互相远程调用对方的本地方法. 并且Yar也提供了并行调用的能力. 可以支持同时调用多个远程服务的方法.


  Yar 是一个轻量级, 高效的​​RPC​​框架, 它提供了一种简单方法来让PHP项目之间可以互相远程调用对方的本地方法. 并且Yar也提供了并行调用的能力. 可以支持同时调用多个远程服务的方法.

 

  情况: 有个业务场景,需要本地项目去调用一个服务层的相关方法实现相应的功能,一般情况,我可以通过普通的http的方式进行请求即可,但是如果只是这个服务是内部使用,那么可以使用rpc的方式进行替代.好处自不必多说,基于tcp传输,支持并发

  实例参考:​​http://hk2.php.net/manual/zh/yar.examples.php​

  结合在项目中使用:

    首先介绍服务端:RpcServer.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;
}
}

  服务端方法:

    

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

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' );
}

}

  客户端调用:

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