• php扩展之Yar


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

  • 相关阅读:
    C++11 std标准库chrono获取系统时间戳
    求取激光光斑质心
    Windows多网卡UDP广播问题
    IP地址分类
    C++各种时间的含义、区别和相互转换
    MFC中控件显示提示信息tooltip
    IP地址 网关 子网掩码之间的关系
    MFC动态添加菜单选项
    opencv函数学习:rotate()的使用
    opencv函数学习:flip()的使用
  • 原文地址:https://www.cnblogs.com/xingxia/p/php_yar.html
Copyright © 2020-2023  润新知