page contents

php扩展之Yar使用

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

attachments-2020-04-kWsF2sAS5e8c396f1b40a.jpg

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

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


首先介绍服务端: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调用


attachments-2020-04-4lzUKjD65e8c3918ade48.jpg

  • 发表于 2020-04-07 16:27
  • 阅读 ( 448 )
  • 分类:PHP开发

你可能感兴趣的文章

相关问题

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1470 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章