first commit
This commit is contained in:
92
vendor/workerman/redis-queue/README.md
vendored
Normal file
92
vendor/workerman/redis-queue/README.md
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
# redis-queue
|
||||
Message queue system written in PHP based on [workerman](https://github.com/walkor/workerman) and backed by Redis.
|
||||
|
||||
# Install
|
||||
```
|
||||
composer require workerman/redis-queue
|
||||
```
|
||||
|
||||
# Usage
|
||||
test.php
|
||||
```php
|
||||
<?php
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Lib\Timer;
|
||||
use Workerman\RedisQueue\Client;
|
||||
|
||||
$worker = new Worker();
|
||||
$worker->onWorkerStart = function () {
|
||||
$client = new Client('redis://127.0.0.1:6379');
|
||||
$client->subscribe('user-1', function($data){
|
||||
echo "user-1\n";
|
||||
var_export($data);
|
||||
});
|
||||
$client->subscribe('user-2', function($data){
|
||||
echo "user-2\n";
|
||||
var_export($data);
|
||||
});
|
||||
Timer::add(1, function()use($client){
|
||||
$client->send('user-1', ['some', 'data']);
|
||||
});
|
||||
};
|
||||
|
||||
Worker::runAll();
|
||||
```
|
||||
|
||||
Run with command `php test.php start` or `php test.php start -d`.
|
||||
|
||||
# API
|
||||
|
||||
* <a href="#construct"><code>Client::<b>__construct()</b></code></a>
|
||||
* <a href="#send"><code>Client::<b>send()</b></code></a>
|
||||
* <a href="#subscribe"><code>Client::<b>subscribe()</b></code></a>
|
||||
* <a href="#unsubscribe"><code>Client::<b>unsubscribe()</b></code></a>
|
||||
|
||||
-------------------------------------------------------
|
||||
|
||||
<a name="construct"></a>
|
||||
### __construct (string $address, [array $options])
|
||||
|
||||
Create an instance by $address and $options.
|
||||
|
||||
* `$address` for example `redis://ip:6379`.
|
||||
|
||||
* `$options` is the client connection options. Defaults:
|
||||
* `auth`: default ''
|
||||
* `db`: default 0
|
||||
* `retry_seconds`: Retry interval after consumption failure
|
||||
* `max_attempts`: Maximum number of retries after consumption failure
|
||||
|
||||
-------------------------------------------------------
|
||||
|
||||
<a name="send"></a>
|
||||
### send(String $queue, Mixed $data, [int $dely=0])
|
||||
|
||||
Send a message to a queue
|
||||
|
||||
* `$queue` is the queue to publish to, `String`
|
||||
* `$data` is the message to publish, `Mixed`
|
||||
* `$dely` is delay seconds for delayed consumption, `Int`
|
||||
|
||||
-------------------------------------------------------
|
||||
|
||||
<a name="subscribe"></a>
|
||||
### subscribe(mixed $queue, callable $callback)
|
||||
|
||||
Subscribe to a queue or queues
|
||||
|
||||
* `$queue` is a `String` queue or an `Array` which has as keys the queue name to subscribe.
|
||||
* `$callback` - `function (Mixed $data)`, `$data` is the data sent by `send($queue, $data)`.
|
||||
|
||||
-------------------------------------------------------
|
||||
|
||||
<a name="unsubscribe"></a>
|
||||
### unsubscribe(mixed $queue)
|
||||
|
||||
Unsubscribe from a queue or queues
|
||||
|
||||
* `$queue` is a `String` queue or an array of queue to unsubscribe from
|
||||
|
||||
-------------------------------------------------------
|
||||
14
vendor/workerman/redis-queue/composer.json
vendored
Normal file
14
vendor/workerman/redis-queue/composer.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name" : "workerman/redis-queue",
|
||||
"type" : "library",
|
||||
"homepage": "http://www.workerman.net",
|
||||
"license" : "MIT",
|
||||
"description": "Message queue system written in PHP based on workerman and backed by Redis.",
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"workerman/redis" : "^1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {"Workerman\\RedisQueue\\": "./src"}
|
||||
}
|
||||
}
|
||||
25
vendor/workerman/redis-queue/examples/test.php
vendored
Normal file
25
vendor/workerman/redis-queue/examples/test.php
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Workerman\Worker;
|
||||
use Workerman\Timer;
|
||||
use Workerman\RedisQueue\Client;
|
||||
|
||||
$worker = new Worker();
|
||||
$worker->onWorkerStart = function () {
|
||||
$client = new Client('redis://127.0.0.1:6379');
|
||||
$client->subscribe('user-1', function($data){
|
||||
echo "user-1\n";
|
||||
var_export($data);
|
||||
});
|
||||
$client->subscribe('user-2', function($data){
|
||||
echo "user-2\n";
|
||||
var_export($data);
|
||||
});
|
||||
Timer::add(1, function()use($client){
|
||||
$client->send('user-1', [666,777]);
|
||||
});
|
||||
};
|
||||
|
||||
Worker::runAll();
|
||||
266
vendor/workerman/redis-queue/src/Client.php
vendored
Normal file
266
vendor/workerman/redis-queue/src/Client.php
vendored
Normal file
@@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of workerman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace Workerman\RedisQueue;
|
||||
|
||||
use RuntimeException;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Redis\Client as Redis;
|
||||
|
||||
/**
|
||||
* Class Client
|
||||
* @package Workerman\RedisQueue
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* Queue waiting for consumption
|
||||
*/
|
||||
const QUEUE_WAITING = '{redis-queue}-waiting';
|
||||
|
||||
/**
|
||||
* Queue with delayed consumption
|
||||
*/
|
||||
const QUEUE_DELAYED = '{redis-queue}-delayed';
|
||||
|
||||
/**
|
||||
* Queue with consumption failure
|
||||
*/
|
||||
const QUEUE_FAILED = '{redis-queue}-failed';
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
protected $_redisSubscribe;
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
protected $_redisSend;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_subscribeQueues = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $_options = [
|
||||
'retry_seconds' => 5,
|
||||
'max_attempts' => 5,
|
||||
'auth' => '',
|
||||
'db' => 0,
|
||||
'prefix' => '',
|
||||
];
|
||||
|
||||
/**
|
||||
* Client constructor.
|
||||
* @param $address
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($address, $options = [])
|
||||
{
|
||||
$this->_redisSubscribe = new Redis($address, $options);
|
||||
$this->_redisSubscribe->brPoping = 0;
|
||||
$this->_redisSend = new Redis($address, $options);
|
||||
if (isset($options['auth']) && $options['auth'] !== '') {
|
||||
$this->_redisSubscribe->auth($options['auth']);
|
||||
$this->_redisSend->auth($options['auth']);
|
||||
}
|
||||
if (isset($options['db'])) {
|
||||
$this->_redisSubscribe->select($options['db']);
|
||||
$this->_redisSend->select($options['db']);
|
||||
}
|
||||
$this->_options = array_merge($this->_options, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send.
|
||||
*
|
||||
* @param $queue
|
||||
* @param $data
|
||||
* @param int $delay
|
||||
* @param callable $cb
|
||||
*/
|
||||
public function send($queue, $data, $delay = 0, $cb = null)
|
||||
{
|
||||
static $_id = 0;
|
||||
$id = \microtime(true) . '.' . (++$_id);
|
||||
$now = time();
|
||||
$package_str = \json_encode([
|
||||
'id' => $id,
|
||||
'time' => $now,
|
||||
'delay' => $delay,
|
||||
'attempts' => 0,
|
||||
'queue' => $queue,
|
||||
'data' => $data
|
||||
]);
|
||||
if (\is_callable($delay)) {
|
||||
$cb = $delay;
|
||||
$delay = 0;
|
||||
}
|
||||
if ($cb) {
|
||||
$cb = function ($ret) use ($cb) {
|
||||
$cb((bool)$ret);
|
||||
};
|
||||
if ($delay == 0) {
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_WAITING . $queue, $package_str, $cb);
|
||||
} else {
|
||||
$this->_redisSend->zAdd($this->_options['prefix'] . static::QUEUE_DELAYED, $now + $delay, $package_str, $cb);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($delay == 0) {
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_WAITING . $queue, $package_str);
|
||||
} else {
|
||||
$this->_redisSend->zAdd($this->_options['prefix'] . static::QUEUE_DELAYED, $now + $delay, $package_str);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe.
|
||||
*
|
||||
* @param string|array $queue
|
||||
* @param callable $callback
|
||||
*/
|
||||
public function subscribe($queue, callable $callback)
|
||||
{
|
||||
$queue = (array)$queue;
|
||||
foreach ($queue as $q) {
|
||||
$redis_key = $this->_options['prefix'] . static::QUEUE_WAITING . $q;
|
||||
$this->_subscribeQueues[$redis_key] = $callback;
|
||||
}
|
||||
$this->pull();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe.
|
||||
*
|
||||
* @param string|array $queue
|
||||
* @return void
|
||||
*/
|
||||
public function unsubscribe($queue)
|
||||
{
|
||||
$queue = (array)$queue;
|
||||
foreach ($queue as $q) {
|
||||
$redis_key = $this->_options['prefix'] . static::QUEUE_WAITING . $q;
|
||||
unset($this->_subscribeQueues[$redis_key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* tryToPullDelayQueue.
|
||||
*/
|
||||
protected function tryToPullDelayQueue()
|
||||
{
|
||||
static $retry_timer = 0;
|
||||
if ($retry_timer) {
|
||||
return;
|
||||
}
|
||||
$retry_timer = Timer::add(1, function () {
|
||||
$now = time();
|
||||
$options = ['LIMIT', 0, 128];
|
||||
$this->_redisSend->zrevrangebyscore($this->_options['prefix'] . static::QUEUE_DELAYED, $now, '-inf', $options, function ($items) {
|
||||
if ($items === false) {
|
||||
throw new RuntimeException($this->_redisSend->error());
|
||||
}
|
||||
foreach ($items as $package_str) {
|
||||
$this->_redisSend->zRem($this->_options['prefix'] . static::QUEUE_DELAYED, $package_str, function ($result) use ($package_str) {
|
||||
if ($result !== 1) {
|
||||
return;
|
||||
}
|
||||
$package = \json_decode($package_str, true);
|
||||
if (!$package) {
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_FAILED, $package_str);
|
||||
return;
|
||||
}
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_WAITING . $package['queue'], $package_str);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* pull.
|
||||
*/
|
||||
public function pull()
|
||||
{
|
||||
$this->tryToPullDelayQueue();
|
||||
if (!$this->_subscribeQueues || $this->_redisSubscribe->brPoping) {
|
||||
return;
|
||||
}
|
||||
$cb = function ($data) use (&$cb) {
|
||||
if ($data) {
|
||||
$this->_redisSubscribe->brPoping = 0;
|
||||
$redis_key = $data[0];
|
||||
$package_str = $data[1];
|
||||
$package = json_decode($package_str, true);
|
||||
if (!$package) {
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_FAILED, $package_str);
|
||||
} else {
|
||||
if (!isset($this->_subscribeQueues[$redis_key])) {
|
||||
// 取消订阅,放回队列
|
||||
$this->_redisSend->rPush($redis_key, $package_str);
|
||||
} else {
|
||||
$callback = $this->_subscribeQueues[$redis_key];
|
||||
try {
|
||||
\call_user_func($callback, $package['data']);
|
||||
} catch (\Exception $e) {
|
||||
if (++$package['attempts'] > $this->_options['max_attempts']) {
|
||||
$package['error'] = (string) $e;
|
||||
$this->fail($package);
|
||||
} else {
|
||||
$this->retry($package);
|
||||
}
|
||||
echo $e;
|
||||
} catch (\Error $e) {
|
||||
if (++$package['attempts'] > $this->_options['max_attempts']) {
|
||||
$package['error'] = (string) $e;
|
||||
$this->fail($package);
|
||||
} else {
|
||||
$this->retry($package);
|
||||
}
|
||||
echo $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->_subscribeQueues) {
|
||||
$this->_redisSubscribe->brPoping = 1;
|
||||
Timer::add(0.000001, [$this->_redisSubscribe, 'brPop'], [\array_keys($this->_subscribeQueues), 1, $cb], false);
|
||||
}
|
||||
};
|
||||
$this->_redisSubscribe->brPoping = 1;
|
||||
$this->_redisSubscribe->brPop(\array_keys($this->_subscribeQueues), 1, $cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
protected function retry($package)
|
||||
{
|
||||
$delay = time() + $this->_options['retry_seconds'] * ($package['attempts']);
|
||||
$this->_redisSend->zAdd($this->_options['prefix'] . static::QUEUE_DELAYED, $delay, \json_encode($package));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $package
|
||||
*/
|
||||
protected function fail($package)
|
||||
{
|
||||
$this->_redisSend->lPush($this->_options['prefix'] . static::QUEUE_FAILED, \json_encode($package));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user