archive
This commit is contained in:
commit
39c8afa223
|
@ -0,0 +1,2 @@
|
|||
vendor/
|
||||
composer.lock
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 CismonX
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,61 @@
|
|||
## Workerman-Amp
|
||||
|
||||
### 概述
|
||||
|
||||
[Amp](http://amphp.org/) 是一个事件驱动的 PHP 框架,与 ReactPHP 类似。
|
||||
|
||||
本项目用于将 Amp 的 event-loop 应用于 Workerman,从而可以在 Workerman 中使用基于 Amp 的高性能组件,例如异步 MySQL,异步 Redis,异步 HTTP 客户端等。
|
||||
|
||||
笔者修改了本项目的源码,使其兼容原生 Amp。现在无需修改 Amp 的源码就可以在项目中使用其所有组件。以后笔者可能会发布一些 Demo。
|
||||
|
||||
### 使用说明
|
||||
|
||||
1. 使用 composer 将 `Workerman\Events\Amp` 加载到项目中。
|
||||
|
||||
```bash
|
||||
composer require cismonx/workerman-amp
|
||||
```
|
||||
|
||||
2. 将 Amp 设置为 Workerman 所使用的 event-loop。如下:
|
||||
|
||||
```php
|
||||
Worker::$eventLoopClass = '\\Workerman\\Events\\Amp';
|
||||
```
|
||||
|
||||
### 兼容 Amp 协程
|
||||
|
||||
在 Amp 中,协程是一个十分重要的特性。对 Workerman 的源码稍加修改,将 watcher 回调中的同步调用(`call_user_func`)改为异步调用(`yield \Amp\call()`),即可在 Workerman 的事件回调函数中直接使用 Amp 协程。
|
||||
|
||||
以下几点需要注意:
|
||||
|
||||
1. 以上提到的对 Workerman 源码的修改在 src 目录下的 coroutine-compatibility.patch 中。这个补丁对 [Workerman-3.4.5-stable](https://github.com/walkor/Workerman/releases/tag/v3.4.5) 有效。
|
||||
|
||||
2. 目前只能在 `onConnect` `onSslHandshake` `onMessage` 这三个事件回调函数中使用 Amp 协程。
|
||||
|
||||
以下是使用 Amp 协程的示例(测试的时候同时多发几条请求,可以看出效果)
|
||||
|
||||
```php
|
||||
use Workerman\Worker;
|
||||
Worker::$eventLoopClass = '\\Workerman\\Events\\Amp';
|
||||
$worker = new Worker('http://[::]:20080');
|
||||
function subtractToZero($init, $interval) {
|
||||
$value = $init;
|
||||
$emitter = new Amp\Emitter;
|
||||
$id = \Workerman\Lib\Timer::add($interval, function () use ($emitter, &$value, &$id) {
|
||||
if ($value > 0)
|
||||
$emitter->emit(--$value);
|
||||
else {
|
||||
$emitter->complete();
|
||||
\Workerman\Lib\Timer::del($id);
|
||||
}
|
||||
});
|
||||
return $emitter->iterate();
|
||||
}
|
||||
$worker->onMessage = function ($connection) {
|
||||
$iterator = subtractToZero(10, 1);
|
||||
while (yield $iterator->advance())
|
||||
var_dump($iterator->getCurrent());
|
||||
$connection->send('ok');
|
||||
};
|
||||
Worker::runAll();
|
||||
```
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name" : "cismonx/workerman-amp",
|
||||
"description" : "Amp event loop for Workerman.",
|
||||
"type" : "library",
|
||||
"require" : {
|
||||
"workerman/workerman": "^3.4",
|
||||
"amphp/amp": "^2.0"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-4" : {
|
||||
"Workerman\\Events\\" : "src/"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Workerman\Events;
|
||||
use Amp\ {function asyncCall, Loop};
|
||||
use Workerman\Worker;
|
||||
|
||||
class Amp implements EventInterface {
|
||||
|
||||
/**
|
||||
* Socket onReadable/onWritable events.
|
||||
* @var array
|
||||
*/
|
||||
protected $_allEvents = [];
|
||||
|
||||
/**
|
||||
* Signals.
|
||||
* @var array
|
||||
*/
|
||||
protected $_eventSignal = [];
|
||||
|
||||
/**
|
||||
* Timers.
|
||||
* @var array
|
||||
*/
|
||||
protected $_eventTimer = [];
|
||||
|
||||
/**
|
||||
* Timer Id counter.
|
||||
* @var int
|
||||
*/
|
||||
protected static $_timerId = 1;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function add($fd, $flag, $func, $args = null) {
|
||||
switch ($flag) {
|
||||
case self::EV_READ:
|
||||
$fd_key = intval($fd);
|
||||
$event = Loop::onReadable($fd, function ($id, $socket) use ($func) {
|
||||
//In Workerman the first parameter should be socket stream.
|
||||
asyncCall($func, $socket);
|
||||
});
|
||||
$this->_allEvents[$fd_key][$flag] = $event;
|
||||
return true;
|
||||
case self::EV_WRITE:
|
||||
$fd_key = intval($fd);
|
||||
$event = Loop::onWritable($fd, function ($id, $socket) use ($func) {
|
||||
//In Workerman the first parameter should be socket stream.
|
||||
asyncCall($func, $socket);
|
||||
});
|
||||
$this->_allEvents[$fd_key][$flag] = $event;
|
||||
return true;
|
||||
case self::EV_SIGNAL:
|
||||
$fd_key = intval($fd);
|
||||
$event = Loop::onSignal($fd, function ($id, $signal) use ($func) {
|
||||
//In Workerman the first parameter should be signal.
|
||||
asyncCall($func, $signal);
|
||||
});
|
||||
$this->_eventSignal[$fd_key] = $event;
|
||||
return true;
|
||||
case self::EV_TIMER:
|
||||
case self::EV_TIMER_ONCE:
|
||||
$param = [$func, (array)$args, $flag, self::$_timerId];
|
||||
$event = Loop::repeat($fd * 1000, \Closure::bind(function () use ($param) {
|
||||
$timer_id = $param[3];
|
||||
if ($param[2] === self::EV_TIMER_ONCE) {
|
||||
//Loop::delay() can also do the trick.
|
||||
Loop::cancel($this->_eventTimer[$timer_id]);
|
||||
unset($this->_eventTimer[$timer_id]);
|
||||
}
|
||||
try {
|
||||
asyncCall($param[0], ...$param[1]);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
} catch (\Error $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
}
|
||||
}, $this, __CLASS__));
|
||||
$this->_eventTimer[self::$_timerId] = $event;
|
||||
return self::$_timerId++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function del($fd, $flag) {
|
||||
switch ($flag) {
|
||||
case self::EV_READ:
|
||||
case self::EV_WRITE:
|
||||
$fd_key = intval($fd);
|
||||
if (isset($this->_allEvents[$fd_key][$flag])) {
|
||||
Loop::cancel($this->_allEvents[$fd_key][$flag]);
|
||||
unset($this->_allEvents[$fd_key][$flag]);
|
||||
}
|
||||
if (empty($this->_allEvents[$fd_key]))
|
||||
unset($this->_allEvents[$fd_key]);
|
||||
break;
|
||||
case self::EV_SIGNAL:
|
||||
$fd_key = intval($fd);
|
||||
if (isset($this->_eventSignal[$fd_key])) {
|
||||
Loop::cancel($this->_eventSignal[$fd_key]);
|
||||
unset($this->_eventSignal[$fd_key]);
|
||||
}
|
||||
break;
|
||||
case self::EV_TIMER:
|
||||
case self::EV_TIMER_ONCE:
|
||||
if (isset($this->_eventTimer[$fd])) {
|
||||
Loop::cancel($this->_eventTimer[$fd]);
|
||||
unset($this->_eventTimer[$fd]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function loop() {
|
||||
Loop::run();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function clearAllTimer() {
|
||||
foreach ($this->_eventTimer as $event)
|
||||
Loop::cancel($event);
|
||||
$this->_eventTimer = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy() {
|
||||
foreach ($this->_eventSignal as $event)
|
||||
Loop::cancel($event);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTimerCount() {
|
||||
return count($this->_eventTimer);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
Index: Connection/TcpConnection.php
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
--- Connection/TcpConnection.php (revision 212aa15bc0edc1226246de52a2fbece0db58414d)
|
||||
+++ Connection/TcpConnection.php (revision )
|
||||
@@ -401,7 +401,7 @@
|
||||
}
|
||||
if (isset($this->onSslHandshake)) {
|
||||
try {
|
||||
- call_user_func($this->onSslHandshake, $this);
|
||||
+ yield \Amp\call($this->onSslHandshake, $this);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
@@ -477,7 +477,7 @@
|
||||
}
|
||||
try {
|
||||
// Decode request buffer before Emitting onMessage callback.
|
||||
- call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
|
||||
+ yield \Amp\call($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
@@ -500,7 +500,7 @@
|
||||
return;
|
||||
}
|
||||
try {
|
||||
- call_user_func($this->onMessage, $this, $this->_recvBuffer);
|
||||
+ yield \Amp\call($this->onMessage, $this, $this->_recvBuffer);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
@@ -526,7 +526,7 @@
|
||||
// Try to emit onBufferDrain callback when the send buffer becomes empty.
|
||||
if ($this->onBufferDrain) {
|
||||
try {
|
||||
- call_user_func($this->onBufferDrain, $this);
|
||||
+ yield \Amp\call($this->onBufferDrain, $this);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
Index: Connection/AsyncTcpConnection.php
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
--- Connection/AsyncTcpConnection.php (revision 212aa15bc0edc1226246de52a2fbece0db58414d)
|
||||
+++ Connection/AsyncTcpConnection.php (revision )
|
||||
@@ -292,7 +292,7 @@
|
||||
// Try to emit onConnect callback.
|
||||
if ($this->onConnect) {
|
||||
try {
|
||||
- call_user_func($this->onConnect, $this);
|
||||
+ yield \Amp\call($this->onConnect, $this);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
@@ -304,7 +304,7 @@
|
||||
// Try to emit protocol::onConnect
|
||||
if (method_exists($this->protocol, 'onConnect')) {
|
||||
try {
|
||||
- call_user_func(array($this->protocol, 'onConnect'), $this);
|
||||
+ yield \Amp\call(array($this->protocol, 'onConnect'), $this);
|
||||
} catch (\Exception $e) {
|
||||
Worker::log($e);
|
||||
exit(250);
|
||||
Index: .gitignore
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
--- .gitignore (revision 212aa15bc0edc1226246de52a2fbece0db58414d)
|
||||
+++ .gitignore (revision )
|
||||
@@ -1,4 +1,5 @@
|
||||
logs
|
||||
+vendor
|
||||
.buildpath
|
||||
.project
|
||||
.settings
|
||||
Index: composer.json
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
--- composer.json (revision 212aa15bc0edc1226246de52a2fbece0db58414d)
|
||||
+++ composer.json (revision )
|
||||
@@ -24,7 +24,8 @@
|
||||
"source": "https://github.com/walkor/workerman"
|
||||
},
|
||||
"require": {
|
||||
- "php": ">=5.3"
|
||||
+ "php": ">=7.0",
|
||||
+ "amphp/amp": "^2.0@dev"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
Index: Worker.php
|
||||
IDEA additional info:
|
||||
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
|
||||
<+>UTF-8
|
||||
===================================================================
|
||||
--- Worker.php (revision 212aa15bc0edc1226246de52a2fbece0db58414d)
|
||||
+++ Worker.php (revision )
|
||||
@@ -1625,7 +1625,7 @@
|
||||
// Try to emit onConnect callback.
|
||||
if ($this->onConnect) {
|
||||
try {
|
||||
- call_user_func($this->onConnect, $connection);
|
||||
+ yield \Amp\call($this->onConnect, $connection);
|
||||
} catch (\Exception $e) {
|
||||
self::log($e);
|
||||
exit(250);
|
||||
@@ -1661,7 +1661,7 @@
|
||||
}
|
||||
ConnectionInterface::$statistics['total_request']++;
|
||||
try {
|
||||
- call_user_func($this->onMessage, $connection, $recv_buffer);
|
||||
+ yield \Amp\call($this->onMessage, $connection, $recv_buffer);
|
||||
} catch (\Exception $e) {
|
||||
self::log($e);
|
||||
exit(250);
|
Reference in New Issue