Showing
4 changed files
with
503 additions
and
0 deletions
Hood/Utils/Rpc/Factory.php
0 → 100644
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 | +} |
Hood/Utils/Rpc/Httpc.php
0 → 100644
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 | + |
Hood/Utils/Rpc/IFace.php
0 → 100644
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 | +} |
Hood/Utils/Rpc/Yar.php
0 → 100644
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 | + |
-
Please register or login to post a comment