   'phpDocumentor\Descriptor\FileDescriptor * hash ee9142d6c0e703377928675fc70bdf89 * pathClient/RpcClient.php	 * source#<?php
/**
 * RPC客户端
 *
 * 请求单个RPC服务
 * $user  = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->getByUid($uid);
 *
 * 批量请求多个RPC服务
 * $rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByUid', $uid);
 * $rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByName', $name);
 * $rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByEmail', $email);
 * $users = RpcClient::goConcurrent($rpc);
 *
 * @author camera360_server@camera360.com
 * @copyright Chengdu pinguo Technology Co.,Ltd.
 */
namespace PG\MSF\Client;

use Exception;
use PG\MSF\Client\Http\Client;
use PG\MSF\Base\Core;

/**
 * Class RpcClient
 * @package PG\MSF\Client
 */
class RpcClient extends Core
{
    /**
     * @var string 当前版本
     */
    public static $version = '0.9';

    /**
     * @var array 所有服务
     */
    protected static $services;

    /**
     * @var string 服务名称
     */
    public $service = '';

    /**
     * @var string host地址，支持 http/https 协议，支持域名地址，支持带端口.
     * http://127.0.0.1 | http://hostname | http://127.0.0.1:80 | http://hostname:80
     * https://127.0.0.1 | https://hostname | https://127.0.0.1:443 | https://hostname:443
     */
    public $host = 'http://127.0.0.1';

    /**
     * @var int 超时时间(单位毫秒)
     */
    public $timeout = 0;

    /**
     * @var string 使用协议，http
     */
    public $scheme = 'http';

    /**
     * @var string url path，如 /path，当不使用rpc模式时，应设置本参数；
     */
    public $urlPath = 'Rpc';

    /**
     * @var string 动作，如GET/POST
     */
    public $verb = 'POST';

    /**
     * @var string handler名称
     */
    public $handler = '';

    /**
     * @var string handler的构造参数
     */
    public $construct = '';

    /**
     * @var string handler的方法名
     */
    public $method = '';

    /**
     * @var array handler的方法参数
     */
    public $args = [];

    /**
     * RpcClient constructor.
     *
     * @param string $service 服务名称，如 'user'
     * @throws Exception
     */
    public function __construct($service)
    {
        $this->service = $service;
        if (isset(static::$services[$service])) {
            // 赋值到类属性中.
            $this->host    = static::$services[$service]['host'];
            $this->verb    = static::$services[$service]['verb'];
            $this->timeout = static::$services[$service]['timeout'];
        } else {
            // 获得配置信息
            /**
             * 'user' = [
             *     'host' => 'http://10.1.90.10:80', <必须>
             *     'timeout' => 1000, <选填，可被下级覆盖>
             * ]
             */
            $config = getInstance()->config->get('params.service.' . $service, []);
            list($root,) = explode('.', $service);
            $config['host'] = getInstance()->config->get('params.service.' . $root . '.host', '');
            if ($config['host'] === '') {
                throw new Exception('Host configuration not found.');
            }

            if (!isset($config['timeout'])) {
                $config['timeout'] = getInstance()->config->get('params.service.' . $root . '.timeout', 0);
            }

            // 赋值到类属性中.
            $this->host    = $config['host'];
            $scheme = substr($this->host, 0, strpos($this->host, ':'));
            if (!in_array($scheme, ['http', 'https'])) {
                throw new Exception('Host configuration invalid.');
            }
            $this->verb = $config['verb'] ?? 'POST';
            $this->timeout = $config['timeout'];

            static::$services[$service]['host']    = $this->host;
            static::$services[$service]['verb']    = $this->verb;
            static::$services[$service]['timeout'] = $this->timeout;
        }
    }

    /**
     * 指定服务句柄，一般为RPC服务导出的类名
     *
     * @param string $handler 服务句柄（类名）
     * @param array|null 服务句柄的构造参数
     * @return RpcClient
     */
    public function handler($handler, $construct = null)
    {
        $this->handler   = $handler;
        $this->construct = $construct;
        return $this;
    }

    /**
     * 拼装handler执行的方法和参数
     *
     * @param string $method handler执行的方法
     * @param array $args handler执行的参数
     * @return $this
     */
    public function func($method, ...$args)
    {
        $this->method = $method;
        $this->args   = $args;
        return $this;
    }

    /**
     * 指定一个远程服务句柄的方法
     *
     * @param string $method 方法名
     * @param mixed $args 执行的参数列表
     * @return mixed
     * @throws \Exception
     */
    public function __call($method, $args)
    {
        $this->method = $method;
        $this->args   = $args;
        $response     = $this->remoteHttpCall($method, $args);

        return $response;
    }

    /**
     * Rpc模式执行远程调用
     *
     * @param string $method 远程服务句柄的方法名
     * @param array $args 执行的参数列表
     * @return mixed
     * @throws Exception
     */
    public function remoteHttpCall($method, array $args)
    {
        $reqParams = [
            'version'   => static::$version,
            'args'      => array_values($args),
            'time'      => microtime(true),
            'handler'   => $this->handler,
            'construct' => $this->construct,
            'method'    => $method
        ];
        $sendData = [
            'data' => getInstance()->pack->pack($reqParams),
        ];
        $headers = [
            'X-RPC' => 1,
        ];

        /**
         * @var Client $client
         */
        $client = $this->getObject(Client::class)->goDnsLookup($this->host, $this->timeout, $headers);
        if (!$client) {
            throw new Exception($this->host . ' dns query failed');
        }

        $urlPath = '/' .$this->urlPath . '/' . $this->service . '/' . $this->handler . '/' . $this->method;
        if ($this->verb == 'POST') {
            $response = $client->goPost($urlPath, $sendData, $this->timeout);
        } else {
            $response = $client->goGet($urlPath, $sendData, $this->timeout);
        }

        if ($response && !empty($response['body']) && ($jsonRes = json_decode($response['body'], true))) {
            if ($response['statusCode'] !== 200) {
                $this->getContext()->getLog()->warning(dump($response, false, true));
                return false;
            } else {
                return $jsonRes;
            }
        } else {
            $this->getContext()->getLog()->warning(dump($response, false, true));
            return false;
        }
    }

    /**
     * 多个RPC并行请求
     *
     * @param array $multiRpc RpcClient实例列表
     * @return array
     */
    public static function goConcurrent(array $multiRpc)
    {
        $results  = [];
        $requests = [];
        $context  = null;

        foreach ($multiRpc as $key => $rpc) {
            if (!($rpc instanceof RpcClient)) {
                $results[$key] = false;
            }

            $reqParams = [
                'version'   => static::$version,
                'args'      => array_values($rpc->args),
                'time'      => microtime(true),
                'handler'   => $rpc->handler,
                'construct' => $rpc->construct,
                'method'    => $rpc->method,
            ];
            $sendData = [
                'data' => getInstance()->pack->pack($reqParams),
            ];

            $requests[$key]['url']         = $rpc->host . '/' . $rpc->urlPath . '/' . $rpc->service . '/' . $rpc->handler . '/' . $rpc->method;
            $requests[$key]['method']      = $rpc->verb;
            $requests[$key]['dns_timeout'] = 2000;
            $requests[$key]['timeout']     = $rpc->timeout;
            $requests[$key]['headers']     = ['X-RPC' => 1];
            $requests[$key]['data']        = $sendData;
            $context                       = $context ?? $rpc->getContext();
        }

        if (empty($requests)) {
            return $results;
        }

        $responses = $context->getObject(Client::class)->goConcurrent($requests);
        foreach ($responses as $key => $response) {
            if ($response && !empty($response['body']) && ($jsonRes = json_decode($response['body'], true))) {
                if ($response['statusCode'] !== 200) {
                    $results[$key] = false;
                    $context->getContext()->getLog()->warning(dump($response, false, true));
                } else {
                    $results[$key] = $jsonRes;
                }
            } else {
                $results[$key] = false;
                $context->getContext()->getLog()->warning(dump($response, false, true));
            }
        }

        return $results;
    }
}
 * namespaceAliases#phpDocumentor\Descriptor\Collection * items	Exception
\ExceptionClient\PG\MSF\Client\Http\ClientCore\PG\MSF\Base\Core * includes	  * constants	  * functions	 
 * classes	\PG\MSF\Client\RpcClient(phpDocumentor\Descriptor\ClassDescriptor	 * parent * implements	  * abstract * final	  * properties	version+phpDocumentor\Descriptor\PropertyDescriptor" * types 
 * default'0.9'	 * static * visibilitypublic * fqsen!\PG\MSF\Client\RpcClient::version * name * namespace 
 * package
 * summary * description * fileDescriptor  * line  * tags	var	 *phpDocumentor\Descriptor\Tag\VarDescriptor * variableName	 .phpDocumentor\Descriptor\Type\StringDescriptor %-)当前版本	 * errors	 2	  * inheritedElement services"   !	protected#"\PG\MSF\Client\RpcClient::services%4& '()* +%,	-	 ./	 3phpDocumentor\Descriptor\Type\UnknownTypeDescriptor%array%-)所有服务2	 2	 3 service" '' !"#!\PG\MSF\Client\RpcClient::service%:& '()* +*,	-	 ./	 0 %-)服务名称2	 2	 3 host" 'http://127.0.0.1' !"#\PG\MSF\Client\RpcClient::host%>& '()* +1,	-	 ./	 0 %-)host地址，支持 http/https 协议，支持域名地址，支持带端口.
http://127.0.0.1 | http://hostname | http://127.0.0.1:80 | http://hostname:80
https://127.0.0.1 | https://hostname | https://127.0.0.1:443 | https://hostname:4432	 2	 3 timeout" 0 !"#!\PG\MSF\Client\RpcClient::timeout%B& '()* +6,	-	 ./	 /phpDocumentor\Descriptor\Type\IntegerDescriptor %-)超时时间(单位毫秒)2	 2	 3 scheme" 'http' !"# \PG\MSF\Client\RpcClient::scheme%G& '()* +;,	-	 ./	 0 %-)使用协议，http2	 2	 3 urlPath" 'Rpc' !"#!\PG\MSF\Client\RpcClient::urlPath%K& '()* +@,	-	 ./	 0 %-)Gurl path，如 /path，当不使用rpc模式时，应设置本参数；2	 2	 3 verb" 'POST' !"#\PG\MSF\Client\RpcClient::verb%O& '()* +E,	-	 ./	 0 %-)动作，如GET/POST2	 2	 3 handler" ; !"#!\PG\MSF\Client\RpcClient::handler%S& '()* +J,	-	 ./	 0 %-)handler名称2	 2	 3 	construct" ; !"##\PG\MSF\Client\RpcClient::construct%V& '()* +O,	-	 ./	 0 %-)handler的构造参数2	 2	 3 method" ; !"# \PG\MSF\Client\RpcClient::method%Y& '()* +T,	-	 ./	 0 %-)handler的方法名2	 2	 3 args" array() !"#\PG\MSF\Client\RpcClient::args%\& '()* +Y,	-	 ./	 7%8%-)handler的方法参数2	 2	 3 
 * methods	__construct)phpDocumentor\Descriptor\MethodDescriptor" !" * arguments	$service+phpDocumentor\Descriptor\ArgumentDescriptor	 * method"	 0   * byReference * isVariadic#%d& '()服务名称，如 'user'* + ,	 2	 3 #'\PG\MSF\Client\RpcClient::__construct()%a& '(RpcClient constructor.)* +a,	param	 ,phpDocumentor\Descriptor\Tag\ParamDescriptor/d"%l)i2	 throws	 -phpDocumentor\Descriptor\Tag\ThrowsDescriptor	 7%%n)2	 return	 2	 3 Sb" !"c	$handleref"	 0  gh#%q& '()服务句柄（类名）* + ,	 2	 3 
$constructef"	 nullgh#%s& '()* + ,	 2	 3 ##\PG\MSF\Client\RpcClient::handler()%S& '(6指定服务句柄，一般为RPC服务导出的类名)* +,	l	 m/q"%l)r2	 m/s	 7%87%t%l)服务句柄的构造参数2	 p	 -phpDocumentor\Descriptor\Tag\ReturnDescriptor	 7%%p)2	 2	 3 funcb" !"c	$methodef"	 0  gh#%z& '()handler执行的方法* + ,	 2	 3 $argsef"	 7%8 gh#%|& '()handler执行的参数* + ,	 2	 3 # \PG\MSF\Client\RpcClient::func()%y& '(%拼装handler执行的方法和参数)* +,	l	 m/z"%l){2	 m/|#%l)}2	 p	 x	 7%$this%p)2	 2	 3 __callb" !"c	zef#	 0  gh#%z& '()	方法名* + ,	 2	 3 |ef#	 7%mixed gh#%|& '()执行的参数列表* + ,	 2	 3 #"\PG\MSF\Client\RpcClient::__call()%& '('指定一个远程服务句柄的方法)* +,	l	 m/z##%l)2	 m/|#+%l)2	 p	 x	 7%%p)2	 n	 o	 7%%n)2	 2	 3 remoteHttpCallb" !"c	zef#N	 0  gh#%z& '()远程服务句柄的方法名* + ,	 2	 3 |ef#N	 7%8 gh#%|& '()* + ,	 2	 3 #*\PG\MSF\Client\RpcClient::remoteHttpCall()%& '(Rpc模式执行远程调用)* +,	l	 m/z#R%l)2	 m/|#Z%l)2	 p	 x	 7%%p)2	 n	 o	 7%%n)2	 2	 3 goConcurrentb" !"c		$multiRpcef#}	 7%8 gh#%& '()RpcClient实例列表* + ,	 2	 3 #(\PG\MSF\Client\RpcClient::goConcurrent()%& '(多个RPC并行请求)* +,	l	 m/#%l)2	 p	 x	 7%8%p)2	 2	 3  * usedTraits	 #%	RpcClient&\PG\MSF\Client'PG\MSF\Client(Class RpcClient)*" +,	package	 &phpDocumentor\Descriptor\TagDescriptor%)2	 
subpackage	 2	 3  * interfaces	 	 * traits	 
 * markers	 #%RpcClient.php& 'Default(RPC客户端)请求单个RPC服务
$user  = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->getByUid($uid);

批量请求多个RPC服务
$rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByUid', $uid);
$rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByName', $name);
$rpc[] = $this->getObject(RpcClient::class, ['user'])->handler('mobile', $construct)->func('getByEmail', $email);
$users = RpcClient::goConcurrent($rpc);* + ,	author	 -phpDocumentor\Descriptor\Tag\AuthorDescriptor%)camera360_server@camera360.com2	 	copyright	 %)"Chengdu pinguo Technology Co.,Ltd.2	 	 %)2	 	 2	 3 