Authored by 沈括号

rpc

  1 +<?php
  2 +namespace Hood\Utils\Rpc;
  3 +
  4 +class Factory
  5 +{
  6 +
  7 + private static $type = null;
  8 +
  9 + /**
  10 + * 返回一个rpc
  11 + * @param $url
  12 + * @param string $type
  13 + * @return Httpc|Yar
  14 + */
  15 + public static function getInstance($url, $type='yar')
  16 + {
  17 + if (strtolower($type) == 'yar') {
  18 + self::$type = 'yar';
  19 + return new Yar($url);
  20 + } else {
  21 + self::$type = 'httpc';
  22 + return new Httpc($url);
  23 + }
  24 + }
  25 +
  26 +
  27 + public static function loop($callback=null, $errorCallback=null)
  28 + {
  29 + if (self::$type == 'yar') {
  30 + Yar::loop($callback, $errorCallback);
  31 + } else {
  32 + Httpc::loop($callback, $errorCallback);
  33 + }
  34 + }
  35 +}
  1 +<?php
  2 +
  3 +
  4 +namespace Hood\Utils\Rpc;
  5 +
  6 +
  7 +class Httpc implements IFace
  8 +{
  9 + protected static $_callbackList = array();
  10 +
  11 +
  12 + protected $_url;
  13 +
  14 + protected $_method;
  15 +
  16 + protected $_concurrent = false;
  17 +
  18 + protected $_format = 'php';
  19 +
  20 + protected $_callback;
  21 +
  22 + protected $_errorCallback;
  23 +
  24 + protected $_timeout = 5;
  25 +
  26 + protected static $loopTimeout = 5;
  27 +
  28 +
  29 + public function __construct($url)
  30 + {
  31 + if (empty($url)) {
  32 + throw new \Exception('调用地址不能为空');
  33 + }
  34 + $this->_url = $url;
  35 + $this->errorCallback(function ($errno, $error, $callinfo) {
  36 +
  37 + });
  38 + }
  39 +
  40 +
  41 + public function __call($method, $args)
  42 + {
  43 + $this->method($method);
  44 + return call_user_func_array(array($this, 'exec'), $args);
  45 + }
  46 +
  47 + /**
  48 + * 设置超时时间(秒)
  49 + * @param $second
  50 + * @return $this
  51 + */
  52 + public function timeout($second)
  53 + {
  54 + if ((int)$second >= 1) {
  55 + $this->_timeout = (int)$second;
  56 + }
  57 + return $this;
  58 + }
  59 +
  60 +
  61 + /**
  62 + * 执行
  63 + * @return array|bool|mixed
  64 + */
  65 + public function exec()
  66 + {
  67 + if (!$this->_concurrent) {
  68 + return $this->single(func_get_args());
  69 + } else {
  70 + if ($this->_format == 'php') {
  71 + $args = serialize(func_get_args());
  72 + } else {
  73 + $args = json_encode(func_get_args());
  74 + }
  75 + Httpc::$_callbackList[] = array(
  76 + 'url' => $this->_url,
  77 + 'callback' => $this->_callback,
  78 + 'format' => $this->_format,
  79 + 'method' => $this->_method,
  80 + 'param' => $args,
  81 + 'timeout' => $this->_timeout,
  82 + 'errorCallback' => $this->_errorCallback
  83 + );
  84 + return true;
  85 + }
  86 + }
  87 +
  88 +
  89 + /**
  90 + * 串行时调用
  91 + * @param $args
  92 + * @return array|mixed
  93 + */
  94 + protected function single($args)
  95 + {
  96 + if ($this->_format == 'php') {
  97 + $args = serialize($args);
  98 + } else {
  99 + $args = json_encode($args);
  100 + }
  101 + $data = array(
  102 + 'method' => $this->_method,
  103 + 'format' => $this->_format,
  104 + 'param' => $args
  105 + );
  106 + $ch = curl_init();
  107 + curl_setopt($ch, CURLOPT_URL, $this->_url);
  108 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  109 + curl_setopt($ch, CURLOPT_HEADER, 0);
  110 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent:HTTPC'));
  111 + curl_setopt($ch, CURLOPT_TIMEOUT, $this->_timeout);
  112 + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
  113 + $output = curl_exec($ch);
  114 +
  115 + $callinfo = curl_getinfo($ch);
  116 + $error = curl_error($ch);
  117 + $errno = curl_errno($ch);
  118 +
  119 + curl_close($ch);
  120 +
  121 +
  122 + if (empty($output)) {
  123 + return array();
  124 + }
  125 + if ($this->_format == 'php') {
  126 + $result = @unserialize($output);
  127 + } else {
  128 + $result = @json_decode($output, true);
  129 + }
  130 +
  131 + if ($result === false) {
  132 + if (!empty($this->_errorCallback)) {
  133 + call_user_func($this->_errorCallback, $errno, $output.$error, $callinfo);
  134 + }
  135 + return $output;
  136 + }
  137 +
  138 + return $result;
  139 + }
  140 +
  141 +
  142 + /**
  143 + * 设置调用的方法
  144 + * @param $method
  145 + * @return $this
  146 + * @throws \Exception
  147 + */
  148 + public function method($method)
  149 + {
  150 + if (empty($method)) {
  151 + throw new \Exception('调用方法不能为空');
  152 + }
  153 + $this->_method = $method;
  154 + return $this;
  155 + }
  156 +
  157 +
  158 + /**
  159 + * 正常返回结果时回调
  160 + * @param $callback
  161 + * @return $this
  162 + */
  163 + public function callback($callback)
  164 + {
  165 + if (!empty($callback) && is_callable($callback)) {
  166 + $this->_callback = $callback;
  167 + }
  168 +
  169 + return $this;
  170 + }
  171 +
  172 +
  173 + /**
  174 + * 调用错误时回调, 回调时传入3个参数,curl的errno, 返回结果+curl错误信息,curl调用的信息
  175 + * @param $errorCallback
  176 + * @return $this
  177 + */
  178 + public function errorCallback($errorCallback)
  179 + {
  180 + if (!empty($errorCallback) && is_callable($errorCallback)) {
  181 + $this->_errorCallback = $errorCallback;
  182 + }
  183 + return $this;
  184 + }
  185 +
  186 +
  187 + /**
  188 + * 是否并行
  189 + * @param bool|false $concurrent
  190 + * @return $this
  191 + */
  192 + public function concurrent($concurrent=false)
  193 + {
  194 + $this->_concurrent = (bool)$concurrent;
  195 + return $this;
  196 + }
  197 +
  198 + /**
  199 + * 返回一个curl资源
  200 + * @param $resources
  201 + * @return resource
  202 + */
  203 + protected static function curl_handle($resources)
  204 + {
  205 + $data = array(
  206 + 'method' => $resources['method'],
  207 + 'format' => $resources['format'],
  208 + 'param' => $resources['param']
  209 + );
  210 + $ch = curl_init();
  211 + curl_setopt($ch, CURLOPT_URL, $resources['url']);
  212 + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  213 + curl_setopt($ch, CURLOPT_HEADER, 0);
  214 + curl_setopt($ch, CURLOPT_HTTPHEADER, array('User-Agent:HTTPC'));
  215 + curl_setopt($ch, CURLOPT_TIMEOUT, $resources['timeout']);
  216 + curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data, '', '&'));
  217 +
  218 + return $ch;
  219 + }
  220 +
  221 +
  222 + /**
  223 + * 发起并行请求
  224 + */
  225 + public static function loop($callback=null, $errorCallback=null)
  226 + {
  227 + $master = curl_multi_init();
  228 + $requestMap = array();
  229 + foreach (self::$_callbackList as $key=>$val) {
  230 + $ch = self::curl_handle($val);
  231 + curl_multi_add_handle($master, $ch);
  232 + $requestMap[(string)$ch] = $key;
  233 + }
  234 +
  235 + do {
  236 +
  237 + while (($execrun = curl_multi_exec($master, $running)) == CURLM_CALL_MULTI_PERFORM) ;
  238 + if ($execrun != CURLM_OK) {
  239 + break;
  240 + }
  241 + // a request was just completed -- find out which one
  242 + while ($done = curl_multi_info_read($master)) {
  243 + // get the info and content returned on the request
  244 + $callinfo = curl_getinfo($done['handle']);
  245 + $output = curl_multi_getcontent($done['handle']);
  246 + $error = curl_error($done['handle']);
  247 + $errno = curl_errno($done['handle']);
  248 + $key = (string) $done['handle'];
  249 + self::$_callbackList[$requestMap[$key]]['output'] = $output;
  250 + self::$_callbackList[$requestMap[$key]]['callinfo'] = $callinfo;
  251 + self::$_callbackList[$requestMap[$key]]['error'] = $error;
  252 + self::$_callbackList[$requestMap[$key]]['errno'] = $errno;
  253 +
  254 + // remove the curl handle that just completed
  255 + curl_multi_remove_handle($master, $done['handle']);
  256 +
  257 + }
  258 +
  259 + if ($running > 0) {
  260 + curl_multi_select($master, self::$loopTimeout);
  261 + }
  262 +
  263 + } while ($running);
  264 +
  265 + curl_multi_close($master);
  266 +
  267 + foreach (self::$_callbackList as $val) {
  268 + if ($val['format'] == 'php' && $val['output'] !== false && $val['callinfo']['http_code'] == 200) {
  269 + $result = @unserialize($val['output']);
  270 + } else if ($val['output'] !== false && $val['callinfo']['http_code'] == 200) {
  271 + $result = @json_decode($val['output'], true);
  272 + } else {
  273 + $result = $val['output'];
  274 + }
  275 + if ($val['callinfo']['http_code'] != 200 && isset($val['errorCallback'])) {
  276 + call_user_func($val['errorCallback'], $val['errno'], $val['output'].$val['error'], $val['callinfo']);
  277 + } else if ($val['callinfo']['http_code'] == 200) {
  278 + call_user_func($val['callback'], $result, $val['callinfo']);
  279 + }
  280 + }
  281 +
  282 + self::$_callbackList = array();
  283 + }
  284 +}
  285 +
  1 +<?php
  2 +namespace Hood\Utils\Rpc;
  3 +
  4 +interface IFace
  5 +{
  6 + public function __call($method, $args);
  7 +
  8 + public function timeout($second);
  9 +
  10 + public function exec();
  11 +
  12 + public function method($method);
  13 +
  14 + public function callback($callback);
  15 +
  16 + public function errorCallback($errorCallback);
  17 +
  18 + public function concurrent($concurrent);
  19 +
  20 + public static function loop($callback, $errorCallback);
  21 +}
  1 +<?php
  2 +
  3 +namespace Hood\Utils\Rpc;
  4 +
  5 +class Yar implements IFace
  6 +{
  7 + protected static $_callbackList = array();
  8 +
  9 +
  10 + protected $_url;
  11 +
  12 + protected $_method;
  13 +
  14 + protected $_concurrent = false;
  15 +
  16 + protected $_format = 'php';
  17 +
  18 + protected $_callback;
  19 +
  20 + protected $_errorCallback;
  21 +
  22 + protected $_timeout = 5;
  23 +
  24 +
  25 + public function __construct($url)
  26 + {
  27 + if (empty($url)) {
  28 + throw new \Exception('地址不能为空!');
  29 + }
  30 + $this->_url = $url;
  31 + $this->errorCallback(function ($errno, $error, $callinfo) {
  32 +
  33 + });
  34 + }
  35 +
  36 + /**
  37 + * 设置要调用的远程方法名称
  38 + * @param $method
  39 + * @return $this
  40 + * @throws \Exception
  41 + */
  42 + public function method($method)
  43 + {
  44 + if (empty($method)) {
  45 + throw new \Exception('方法不能为空');
  46 + }
  47 + $this->_method = $method;
  48 + return $this;
  49 + }
  50 +
  51 +
  52 + /**
  53 + * 设置超时时间(秒)
  54 + * @param $second
  55 + * @return $this
  56 + */
  57 + public function timeout($second)
  58 + {
  59 + if ((int)$second >= 1) {
  60 + $this->_timeout = (int)$second * 1000;
  61 + }
  62 + return $this;
  63 + }
  64 +
  65 +
  66 + /**
  67 + * 远程调用
  68 + * @param $method
  69 + * @param $args
  70 + */
  71 + public function __call($method, $args)
  72 + {
  73 + $this->method($method);
  74 + return call_user_func_array(array($this, 'exec'), $args);
  75 + }
  76 +
  77 +
  78 + /**
  79 + * 设置回调方法
  80 + * @param $callback
  81 + * @return $this
  82 + * @throws \Exception
  83 + */
  84 + public function callback($callback)
  85 + {
  86 + if (!empty($callback) && is_callable($callback)) {
  87 + $this->_callback = $callback;
  88 + }
  89 +
  90 + return $this;
  91 + }
  92 +
  93 +
  94 + /**
  95 + * 异常时的回调方法
  96 + * @param $errorCallback
  97 + * @return $this
  98 + * @throws \Exception
  99 + */
  100 + public function errorCallback($errorCallback)
  101 + {
  102 + if (!empty($errorCallback) && is_callable($errorCallback)) {
  103 + $this->_errorCallback = $errorCallback;
  104 + }
  105 +
  106 + return $this;
  107 + }
  108 +
  109 + /**
  110 + * 设置是否并行
  111 + * @param $concurrent
  112 + * @return $this
  113 + */
  114 + public function concurrent($concurrent)
  115 + {
  116 + $this->_concurrent = (bool)$concurrent;
  117 + return $this;
  118 + }
  119 +
  120 +
  121 + /**
  122 + * 串行时使用的执行,参数按接口的参数顺序
  123 + * @return bool
  124 + */
  125 + public function exec()
  126 + {
  127 + if (empty($this->_method)) {
  128 + throw new \Exception('远程调用方法不能为空!');
  129 + }
  130 + if ($this->_concurrent) {//并行
  131 + \Yar_Concurrent_Client::call($this->_url, $this->_method, func_get_args(), $this->_callback, $this->_errorCallback, array('YAR_OPT_TIMEOUT'=>$this->_timeout, 'YAR_OPT_CONNECT_TIMEOUT'=>$this->_timeout));
  132 + return true;
  133 + } else {//串行
  134 + $client = new \Yar_Client($this->_url);
  135 + $client->SetOpt(YAR_OPT_CONNECT_TIMEOUT, $this->_timeout);
  136 + $client->SetOpt(YAR_OPT_TIMEOUT, $this->_timeout);
  137 + try {
  138 + return call_user_func_array(array($client, $this->_method), func_get_args());
  139 + } catch (\Exception $e) {
  140 + if (!empty($this->_errorCallback)) {
  141 + call_user_func_array($this->_errorCallback, array($e, $e, $e));
  142 + }
  143 + return $e->getMessage();
  144 + }
  145 + }
  146 + }
  147 +
  148 +
  149 +
  150 + /**
  151 + * 发起请求
  152 + * @param null $callback
  153 + * @param null $errorCallback
  154 + */
  155 + public static function loop($callback=null, $errorCallback=null)
  156 + {
  157 + \Yar_Concurrent_Client::loop($callback, $errorCallback);
  158 + }
  159 +
  160 +}
  161 +
  162 +