uoload
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
docker/
|
||||
.idea
|
||||
/vendor/
|
||||
composer.lock
|
||||
/Tests/config.php
|
||||
runtime
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 sunnywoon
|
||||
|
||||
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,41 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 16:40
|
||||
*/
|
||||
|
||||
namespace Tests;
|
||||
|
||||
class TestHandler extends \WorkerManQueue\Handler\JobHandler
|
||||
{
|
||||
/**
|
||||
* 失败回调方法
|
||||
* @param \WorkerManQueue\Job $job 任务
|
||||
* @param string $func 执行的方法
|
||||
* @param array $data 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function failed(\WorkerManQueue\Job $job, $func, $data)
|
||||
{
|
||||
\WorkerManQueue\Helpers\Log::info('failed run handler -- func: ' . $func . ' -- params: ' . json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务成功回调
|
||||
* @param \WorkerManQueue\Job $job 任务
|
||||
* @param string $func 执行的方法
|
||||
* @param array $data 参数
|
||||
* @return mixed
|
||||
*/
|
||||
public function success(\WorkerManQueue\Job $job, $func, $data)
|
||||
{
|
||||
\WorkerManQueue\Helpers\Log::info('success run handler -- func: ' . $func . ' -- params: ' . json_encode($data));
|
||||
}
|
||||
|
||||
|
||||
public function test(\WorkerManQueue\Job $job, $data)
|
||||
{
|
||||
$res = 'success';
|
||||
\WorkerManQueue\Helpers\Log::info('run handler -- func: test -- params: ' . json_encode($data) . '; result : ' . var_export($res, true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
require dirname(__DIR__)."/vendor/autoload.php";
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'log' => [
|
||||
'logRoot' => __DIR__ . '/../runtime/log',
|
||||
'fileName' => '\q\u\e\u\e_Y-m-d.\l\o\g',
|
||||
],
|
||||
|
||||
'connectList' => [
|
||||
'Redis' => [
|
||||
'class' => '\\WorkerManQueue\\Connection\\Redis\\Redis',
|
||||
'config' => [
|
||||
'popTimeout' => 3, // pop阻塞的超时时长 s
|
||||
'host' => '127.0.0.1', // 数据库地址
|
||||
'port' => 6379, // 数据库端口
|
||||
'db' => 0, // 库
|
||||
'password' => null, // 密码
|
||||
'connTimeout' => 1, // 链接超时
|
||||
],
|
||||
]
|
||||
],
|
||||
|
||||
'currentConnect' => 'Redis',
|
||||
];
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
require __DIR__ . "/bootstrap.php";
|
||||
require __DIR__ . "/TestHandler.php";
|
||||
|
||||
$config = include __DIR__ . '/config.php';
|
||||
|
||||
$queue = \WorkerManQueue\Queue::getInstance('Redis', $config);
|
||||
|
||||
use Tests\TestHandler;
|
||||
|
||||
for ($i = 0; $i <= 100; $i++) {
|
||||
$r = $queue->pushOn(new TestHandler(), 'test', ['test' => $i], 'queue');
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
require __DIR__."/bootstrap.php";
|
||||
|
||||
$config = include __DIR__.'/config.php';
|
||||
|
||||
$worker = new \WorkerManQueue\Worker($config);
|
||||
|
||||
$worker->run();
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "woon/workerman-queue",
|
||||
"require": {
|
||||
"workerman/workerman": "^4.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WorkerManQueue\\": "src/",
|
||||
"Tests\\": "Tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:02
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Connection;
|
||||
|
||||
use WorkerManQueue\Exception;
|
||||
use WorkerManQueue\Job;
|
||||
|
||||
/**
|
||||
* 连接类
|
||||
* Class Connection
|
||||
* @package WorkerManQueue\Connection
|
||||
*/
|
||||
abstract class Connection
|
||||
{
|
||||
/**
|
||||
* config
|
||||
* @var array
|
||||
*/
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* singleton
|
||||
* @var Connection
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* pop阻塞超时时长
|
||||
* @var int
|
||||
*/
|
||||
public $popTimeOut = 3;
|
||||
|
||||
/**
|
||||
* 处理程序
|
||||
* @var \Closure
|
||||
*/
|
||||
protected $handler = null;
|
||||
|
||||
/**
|
||||
* Connection constructor.
|
||||
* @param array $config 配置参数
|
||||
*/
|
||||
protected function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
if (isset($config['popTimeout']) && $config['popTimeout'] > 0) {
|
||||
$this->popTimeOut = $config['popTimeout'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置处理程序
|
||||
* @param \Closure $handler
|
||||
*/
|
||||
public function setHandler(\Closure $handler)
|
||||
{
|
||||
$this->handler = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connection destruct.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->close();
|
||||
static::$instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不允许被克隆
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function __clone()
|
||||
{
|
||||
throw new Exception("This class cannot be cloned", -101);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单例
|
||||
* @param array $config 配置参数
|
||||
* @return Connection|null
|
||||
*/
|
||||
public static function getInstance($config = [])
|
||||
{
|
||||
if (!(static::$instance instanceof Connection)) {
|
||||
static::$instance = new static($config);
|
||||
}
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function close();
|
||||
|
||||
/**
|
||||
* 执行pop出来的任务(阻塞方法)
|
||||
* @param string $queueName
|
||||
*/
|
||||
public function popRun($queueName, $popTimeOut = 0)
|
||||
{
|
||||
$extends = [];
|
||||
$job = $this->pop($queueName, $popTimeOut, $extends);
|
||||
if ($job instanceof Job) {
|
||||
// 执行任务
|
||||
$this->runJob($job, $queueName);
|
||||
|
||||
// 确认任务
|
||||
$this->ack($queueName, $job, $extends);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @param Job $job
|
||||
* @param $queueName
|
||||
*/
|
||||
public function runJob(Job $job, $queueName)
|
||||
{
|
||||
// 执行任务
|
||||
$handler = $this->handler;
|
||||
$handler($job, $queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出队头任务(blocking)
|
||||
* @param string $queueName 队列名称
|
||||
* @param int $popTimeOut 阻塞时间
|
||||
* @param array & $extends 额外需要传递给ack方法的参数
|
||||
* @return Job|null
|
||||
*/
|
||||
abstract protected function pop($queueName, $popTimeOut = 0, &$extends = []);
|
||||
|
||||
/**
|
||||
* 确认任务
|
||||
* @param string $queueName
|
||||
* @param Job $job
|
||||
* @param array $extends
|
||||
*/
|
||||
abstract protected function ack($queueName, Job $job = null, $extends = []);
|
||||
|
||||
/**
|
||||
* 压入队列
|
||||
* @param Job $job
|
||||
* @param String $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function push(Job $job, $queueName);
|
||||
|
||||
|
||||
/**
|
||||
* 添加一条延迟任务
|
||||
* @param int $delay 延迟的秒数
|
||||
* @param Job $job 任务
|
||||
* @param String $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
abstract public function later($delay, Job $job, $queueName);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:12
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Connection;
|
||||
|
||||
|
||||
use WorkerManQueue\Helpers\Log;
|
||||
|
||||
class ConnectionFactory
|
||||
{
|
||||
/**
|
||||
* @var array 链接配置列表
|
||||
* example:
|
||||
*
|
||||
*/
|
||||
public static $connectList = [
|
||||
'Redis' => [
|
||||
'class' => '\\WorkerManQueue\\Connection\\Redis\\Redis',
|
||||
'config' => [
|
||||
'popTimeout' => 3, // pop阻塞的超时时长 s
|
||||
'host' => '127.0.0.1', // 数据库地址
|
||||
'port' => 6379, // 数据库端口
|
||||
'db' => 0, // 库
|
||||
'password' => null, // 密码
|
||||
'connTimeout' => 1, // 链接超时
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string 当前默认使用的链接
|
||||
*/
|
||||
public static $currentConnect = 'Redis';
|
||||
|
||||
/**
|
||||
* 获取链接对象
|
||||
* @param string $currentName 当前链接方式
|
||||
* @return Connection
|
||||
*/
|
||||
public static function getInstance($currentName)
|
||||
{
|
||||
$connect = isset(self::$connectList[$currentName]) ? self::$connectList[$currentName] : [];
|
||||
if (empty($connect) || !isset($connect['class']) || empty($connect['class'])) {
|
||||
Log::error('There is no connection available type');
|
||||
return null;
|
||||
} else {
|
||||
$class = $connect['class'];
|
||||
$config = isset($connect['config']) ? $connect['config'] : [];
|
||||
return $class::getInstance($config);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:16
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Connection\Redis;
|
||||
|
||||
|
||||
use WorkerManQueue\Connection\Connection;
|
||||
use WorkerManQueue\Job;
|
||||
|
||||
class Redis extends Connection
|
||||
{
|
||||
/**
|
||||
* redis单例对象
|
||||
* @var Redis
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
/**
|
||||
* redis host
|
||||
* @var string
|
||||
*/
|
||||
private $host = '127.0.0.1';
|
||||
|
||||
/**
|
||||
* redis port
|
||||
* @var int
|
||||
*/
|
||||
private $port = 6379;
|
||||
|
||||
/**
|
||||
* redis database
|
||||
* @var int
|
||||
*/
|
||||
private $database = 0;
|
||||
|
||||
/**
|
||||
* redis password
|
||||
* @var string
|
||||
*/
|
||||
private $password = '';
|
||||
|
||||
/**
|
||||
* redis timeout
|
||||
* @var int
|
||||
*/
|
||||
private $connTimeout = 0;
|
||||
|
||||
/**
|
||||
* redis驱动
|
||||
* @var \Redis
|
||||
*/
|
||||
private $connect = null;
|
||||
|
||||
/**
|
||||
* 本类单例
|
||||
* @var Redis
|
||||
*/
|
||||
/**
|
||||
* Connection constructor.
|
||||
* @param array $config 配置参数
|
||||
*/
|
||||
protected function __construct(array $config = [])
|
||||
{
|
||||
parent::__construct($config);
|
||||
$this->host = (isset($config['host']) && !empty($config['host'])) ? $config['host'] : $this->host;
|
||||
$this->port = (isset($config['port']) && !empty($config['port'])) ? $config['port'] : $this->port;
|
||||
$this->database = (isset($config['db']) && !empty($config['db'])) ? $config['db'] : $this->database;
|
||||
$this->password = (isset($config['password']) && !empty($config['password'])) ? $config['password'] : $this->password;
|
||||
$this->connTimeout = (isset($config['connTimeout']) && !empty($config['connTimeout'])) ? $config['connTimeout'] : $this->connTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* get connect
|
||||
* @return \Redis
|
||||
*/
|
||||
private function getConnect()
|
||||
{
|
||||
if (empty($this->connect)) {
|
||||
$this->connect = new \Redis();
|
||||
$this->open();
|
||||
} else {
|
||||
if (@$this->connect->ping() !== '+PONG') {
|
||||
$this->open();
|
||||
}
|
||||
}
|
||||
return $this->connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* open redis connect
|
||||
*/
|
||||
private function open()
|
||||
{
|
||||
$this->connect->connect($this->host, $this->port, $this->connTimeout);
|
||||
if (!empty($this->password)) {
|
||||
$this->connect->auth($this->password);
|
||||
}
|
||||
$this->connect->select($this->database);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
* @return boolean
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
if (!empty($this->connect)) {
|
||||
$this->connect->close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出队头任务(blocking)
|
||||
* @param string $queueName 队列名称
|
||||
* @param int $popTimeOut 阻塞时间
|
||||
* @param array & $extends 额外需要传递给ack方法的参数
|
||||
* @return Job|null
|
||||
*/
|
||||
protected function pop($queueName, $popTimeOut = 0, &$extends = [])
|
||||
{
|
||||
//从延迟集合中合并到主执行队列
|
||||
$this->migrateAllExpiredJobs($queueName);
|
||||
|
||||
if ($popTimeOut) {
|
||||
$jobStr = $this->getConnect()->blPop($queueName, $popTimeOut);
|
||||
if (isset($jobStr[1]) && !empty($jobStr[1])) {
|
||||
$jobStr = $jobStr[1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
$jobStr = $this->getConnect()->lPop($queueName);
|
||||
}
|
||||
|
||||
if (empty($jobStr)) {
|
||||
return null;
|
||||
} else {
|
||||
return Job::Decode($jobStr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认任务
|
||||
* @param string $queueName
|
||||
* @param Job $job
|
||||
* @param array $extends
|
||||
*/
|
||||
public function ack($queueName, Job $job = null, $extends = [])
|
||||
{
|
||||
// redis不需要确认任务
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 压入队列(直接压入主执行队列)
|
||||
*
|
||||
* @param Job $job
|
||||
* @param string $queueName 队列名称
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function push(Job $job, $queueName)
|
||||
{
|
||||
//命令:rpush 队列名 任务
|
||||
$length = $this->getConnect()->rPush($queueName, Job::Encode($job));
|
||||
if ($length) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条延迟任务
|
||||
* 放入等待执行任务的有序集合中
|
||||
*
|
||||
* @param int $delay 延迟的秒数
|
||||
* @param Job $job 任务
|
||||
* @param string $queueName 队列名称
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function later($delay, Job $job, $queueName)
|
||||
{
|
||||
//命令:zadd 主队列名:delayed 当前时间戳+延迟秒数 任务
|
||||
$result = $this->getConnect()->zAdd($queueName . ':delayed', time() + $delay, Job::Encode($job));
|
||||
if ($result) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 合并等待执行的任务
|
||||
* @param string $queueName
|
||||
* @return void
|
||||
*/
|
||||
protected function migrateAllExpiredJobs($queueName)
|
||||
{
|
||||
$this->migrateExpiredJobs($queueName . ':delayed', $queueName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 当延时任务到大执行时间时,将延时任务从延时任务集合中移动到主执行队列中
|
||||
* @param string $from 集合名称
|
||||
* @param string $to 队列名称
|
||||
* @return void
|
||||
*/
|
||||
protected function migrateExpiredJobs($from, $to)
|
||||
{
|
||||
$time = time();
|
||||
$jobs = $this->getExpiredJobs($from, $time);
|
||||
if (count($jobs) > 0) {
|
||||
$connect = $this->getConnect();
|
||||
//开始redis事物
|
||||
$connect->watch($from);
|
||||
$connect->multi();
|
||||
|
||||
$this->removeExpiredJobs($from, $time);
|
||||
$this->pushExpiredJobsOntoNewQueue($to, $jobs);
|
||||
|
||||
$connect->exec();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从指定集合中获取所有超时的任务
|
||||
* @param String $name 集合名称
|
||||
* @param int $time 超时时间(集合中小于该时间为超时)
|
||||
* @return mixed
|
||||
*/
|
||||
public function getExpiredJobs($name, $time)
|
||||
{
|
||||
return $this->getConnect()->zRangeByScore($name, '-inf', $time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 从指定集合删除过期任务
|
||||
* @param string $from
|
||||
* @param int $time
|
||||
* @return void
|
||||
*/
|
||||
protected function removeExpiredJobs($from, $time)
|
||||
{
|
||||
$this->getConnect()->zRemRangeByScore($from, '-inf', $time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将多个任务从添加到队列
|
||||
*
|
||||
* 场景:将有序集合中的延迟任务入主队列
|
||||
* @param string $to
|
||||
* @param array $jobs
|
||||
* @return void
|
||||
*/
|
||||
protected function pushExpiredJobsOntoNewQueue($to, $jobs)
|
||||
{
|
||||
//等价于 $connect->rPush($to,$jobs[0],$jobs[1]... );
|
||||
$connect = $this->getConnect();
|
||||
call_user_func_array([$connect, 'rPush'], array_merge([$to], $jobs));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:01
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue;
|
||||
|
||||
|
||||
class Exception extends \Exception
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:07
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Handler;
|
||||
|
||||
use WorkerManQueue\Job;
|
||||
|
||||
abstract class JobHandler
|
||||
{
|
||||
/**
|
||||
* 回调执行任务方法
|
||||
* @param Job $job 任务
|
||||
* @param String $func 执行的方法
|
||||
* @param array $data 参数
|
||||
* @return void
|
||||
*/
|
||||
public function handler(Job $job, $func, $data)
|
||||
{
|
||||
try {
|
||||
if (method_exists($this, $func)) {
|
||||
$this->$func($job, $data);
|
||||
} else {
|
||||
$job->setForceFailure('method "' . $func . '" does not exist');
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$job->setOnceFailure($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 失败回调方法
|
||||
* @param Job $job 任务
|
||||
* @param string $func 执行的方法
|
||||
* @param array $data 参数
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function failed(Job $job, $func, $data);
|
||||
|
||||
|
||||
/**
|
||||
* 任务成功回调
|
||||
* @param Job $job 任务
|
||||
* @param string $func 执行的方法
|
||||
* @param array $data 参数
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function success(Job $job, $func, $data);
|
||||
|
||||
|
||||
/**
|
||||
* 回调方法
|
||||
* @param $job
|
||||
* @param $data
|
||||
*/
|
||||
/**
|
||||
* public function func($job,$data){}
|
||||
*/
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:14
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Helpers;
|
||||
|
||||
trait LoadConfig
|
||||
{
|
||||
/**
|
||||
* 允许配置的变量名
|
||||
* @var array
|
||||
*/
|
||||
protected $configNameList = [];
|
||||
|
||||
/**
|
||||
* 加载配置
|
||||
* @param array $config
|
||||
* @return $this
|
||||
*/
|
||||
public function setConfig(array $config)
|
||||
{
|
||||
foreach ($config as $k => $v) {
|
||||
if (in_array($k, $this->configNameList)) {
|
||||
if (!is_null($v)) {
|
||||
$this->$k = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:13
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue\Helpers;
|
||||
|
||||
class Log
|
||||
{
|
||||
use LoadConfig;
|
||||
|
||||
/**
|
||||
* 文件路径
|
||||
* @var string
|
||||
*/
|
||||
protected $logRoot = __DIR__.'/../../runtime/log';
|
||||
|
||||
/**
|
||||
* 文件名
|
||||
* @var string
|
||||
*/
|
||||
protected $fileName = '\q\u\e\u\e_Y-m-d.\l\o\g';
|
||||
|
||||
/**
|
||||
* @var self
|
||||
*/
|
||||
protected static $instance = null;
|
||||
|
||||
protected function __construct() {
|
||||
$this->configNameList = ['logRoot', 'fileName'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Log
|
||||
*/
|
||||
public static function getInstance()
|
||||
{
|
||||
if (!(self::$instance instanceof self)) {
|
||||
self::$instance = new Log();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $level
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
protected function write($level, $message = '', $extends = [])
|
||||
{
|
||||
if (!is_dir($this->logRoot)) {
|
||||
$mkdir = mkdir($this->logRoot, 0777, true);
|
||||
if (!$mkdir) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$time = time();
|
||||
|
||||
$filePath = rtrim($this->logRoot, '/') . '/' . date($this->fileName, $time);
|
||||
|
||||
// 信息
|
||||
$string = sprintf(
|
||||
"[%s][%s]: %s; extends=%s \n",
|
||||
$level, date('Y-m-d m:d:s', $time), $message, json_encode($extends)
|
||||
);
|
||||
|
||||
if (file_put_contents($filePath, $string, FILE_APPEND)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* notice
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
public static function notice($message = '', $extends = [])
|
||||
{
|
||||
return self::getInstance()->write('notice', $message, $extends);
|
||||
}
|
||||
|
||||
/**
|
||||
* info
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
public static function info($message = '', $extends = [])
|
||||
{
|
||||
return self::getInstance()->write('info', $message, $extends);
|
||||
}
|
||||
|
||||
/**
|
||||
* warning
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
public static function warning($message = '', $extends = [])
|
||||
{
|
||||
return self::getInstance()->write('warning', $message, $extends);
|
||||
}
|
||||
|
||||
/**
|
||||
* error
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
public static function error($message = '', $extends = [])
|
||||
{
|
||||
return self::getInstance()->write('error', $message, $extends);
|
||||
}
|
||||
|
||||
/**
|
||||
* fatal
|
||||
* @param string $message
|
||||
* @param array $extends
|
||||
* @return bool
|
||||
*/
|
||||
public static function fatal($message = '', $extends = [])
|
||||
{
|
||||
return self::getInstance()->write('fatal', $message, $extends);
|
||||
}
|
||||
}
|
||||
+243
@@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:07
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue;
|
||||
|
||||
|
||||
use WorkerManQueue\Handler\JobHandler;
|
||||
|
||||
class Job
|
||||
{
|
||||
/**
|
||||
* @var string 工作ID(唯一)
|
||||
*/
|
||||
public $id = '';
|
||||
|
||||
/******************* handler ********************/
|
||||
/**
|
||||
* @var JobHandler job handler
|
||||
*/
|
||||
protected $handler;
|
||||
/**
|
||||
* @var String 执行的方法
|
||||
*/
|
||||
protected $func;
|
||||
/**
|
||||
* @var array 执行方法的参数
|
||||
*/
|
||||
protected $param;
|
||||
|
||||
|
||||
/******************* run ********************/
|
||||
/**
|
||||
* 最后一次执行状态 true成功 false失败
|
||||
* @var bool
|
||||
*/
|
||||
protected $lastStatus = true;
|
||||
/**
|
||||
* @var bool 是否强制失败(强制失败不会重试)
|
||||
*/
|
||||
protected $forceFailed = false;
|
||||
/**
|
||||
* @var string[] 异常信息数组
|
||||
*/
|
||||
protected $errorArr = [];
|
||||
|
||||
/**
|
||||
* @var boolean 当前是否执行成功
|
||||
*/
|
||||
protected $isExec;
|
||||
/**
|
||||
* @var int 当前已经执行次数
|
||||
*/
|
||||
protected $attempts;
|
||||
|
||||
/**
|
||||
* @param JobHandler $handler 回调类
|
||||
* @param String $func 回调类中的回调方法名
|
||||
* @param mixed $param 该回调方法需要的参数
|
||||
*/
|
||||
public function __construct(JobHandler $handler, $func, $param)
|
||||
{
|
||||
$this->resetId();
|
||||
|
||||
$this->handler = $handler;
|
||||
$this->func = $func;
|
||||
$this->param = $param;
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成id
|
||||
*/
|
||||
public function resetId()
|
||||
{
|
||||
$this->id = md5(uniqid(rand(0, 9999) . microtime(true), true));
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化默认任务参数
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
$this->isExec = false;
|
||||
$this->attempts = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 该任务已经执行的次数
|
||||
* @return int
|
||||
*/
|
||||
public function getAttempts()
|
||||
{
|
||||
return $this->attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务失败回调
|
||||
* @return void
|
||||
*/
|
||||
public function failed()
|
||||
{
|
||||
$this->handler->failed($this, $this->func, $this->param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务成功回调
|
||||
* @return void
|
||||
*/
|
||||
public function success()
|
||||
{
|
||||
$this->handler->success($this, $this->func, $this->param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @return void
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$this->attempts++;
|
||||
|
||||
$this->resetStatus();
|
||||
|
||||
//执行handler回调
|
||||
$this->handler->handler($this, $this->func, $this->param);
|
||||
|
||||
if ($this->lastStatus) {
|
||||
$this->isExec = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 重制执行状态
|
||||
*/
|
||||
protected function resetStatus()
|
||||
{
|
||||
$this->lastStatus = true;
|
||||
$this->forceFailed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本次执行失败(会重试)
|
||||
* @param string $message 错误信息
|
||||
*/
|
||||
public function setOnceFailure($message = "")
|
||||
{
|
||||
$this->lastStatus = false;
|
||||
$this->forceFailed = false;
|
||||
$this->errorArr[] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置本次任务为强制失败(不会重试)
|
||||
* @param string $message 错误信息
|
||||
*/
|
||||
public function setForceFailure($message = "")
|
||||
{
|
||||
$this->lastStatus = false;
|
||||
$this->forceFailed = true;
|
||||
$this->errorArr[] = $message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务是否执行成功
|
||||
* @return boolean
|
||||
*/
|
||||
public function isExec()
|
||||
{
|
||||
return $this->isExec;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取任务失败的异常信息数组
|
||||
* @return string[]
|
||||
*/
|
||||
public function getErrors()
|
||||
{
|
||||
return $this->errorArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否需要重试该任务
|
||||
* @param int $maxAttempt
|
||||
* @return bool
|
||||
*/
|
||||
public function isRetry(int $maxAttempt)
|
||||
{
|
||||
if ($this->isExec()) {
|
||||
// 执行成功不需要重试
|
||||
return false;
|
||||
} else {
|
||||
if (
|
||||
// 判断是否强制失败,如果强制失败,则任务不需要重试
|
||||
($this->forceFailed) ||
|
||||
// 判断当前是否超出指定执行次数,如果超过最大限制,则任务不需要重试
|
||||
($maxAttempt > 0 && $this->getAttempts() >= $maxAttempt)
|
||||
) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试该任务
|
||||
* @param Queue $queue 队列
|
||||
* @param string $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
public function reTry(Queue $queue, $queueName)
|
||||
{
|
||||
return $queue->push($this, $queueName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 序列化对象
|
||||
* @param Job $job
|
||||
* @return string
|
||||
*/
|
||||
public static function Encode(Job $job)
|
||||
{
|
||||
return base64_encode(serialize($job));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 反序列化对象
|
||||
* @param string $jobStr
|
||||
* @return Job
|
||||
*/
|
||||
public static function Decode($jobStr)
|
||||
{
|
||||
return unserialize(base64_decode($jobStr));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:35
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue;
|
||||
|
||||
|
||||
use WorkerManQueue\Connection\ConnectionFactory;
|
||||
use WorkerManQueue\Helpers\Log;
|
||||
|
||||
class Load
|
||||
{
|
||||
/**
|
||||
* 加载queue模块依赖的配置
|
||||
* @param array $config
|
||||
*/
|
||||
public static function Queue(array $config)
|
||||
{
|
||||
// 加载log
|
||||
if (isset($config['log'])) {
|
||||
Log::getInstance()->setConfig($config['log']);
|
||||
}
|
||||
|
||||
// 加载链接列表
|
||||
if (isset($config['connectList'])) {
|
||||
ConnectionFactory::$connectList = $config['connectList'];
|
||||
}
|
||||
|
||||
// 加载当前链接
|
||||
if (isset($config['currentConnect'])) {
|
||||
ConnectionFactory::$currentConnect = $config['currentConnect'];
|
||||
}
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 14:54
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue;
|
||||
|
||||
use WorkerManQueue\Connection\Connection;
|
||||
use WorkerManQueue\Connection\ConnectionFactory;
|
||||
use WorkerManQueue\Handler\JobHandler;
|
||||
|
||||
class Queue
|
||||
{
|
||||
/**
|
||||
* @var array [Queue] 单例对象数组
|
||||
*/
|
||||
protected static $instances;
|
||||
|
||||
/**
|
||||
* @var Connection 连接对象
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Queue constructor.
|
||||
* @param Connection $connection
|
||||
*/
|
||||
protected function __construct(Connection $connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
static::$instances = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 不允许被克隆
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function __clone()
|
||||
{
|
||||
throw new Exception("This class cannot be cloned", -101);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connectName 链接类型(默认走配置)
|
||||
* @return Queue|null
|
||||
*/
|
||||
public static function getInstance($connectName = '', $config = [])
|
||||
{
|
||||
if (!empty($config)) {
|
||||
Load::Queue($config);
|
||||
}
|
||||
|
||||
if (empty($connectName)) {
|
||||
$connectName = ConnectionFactory::$currentConnect;
|
||||
}
|
||||
if (!isset(static::$instances[$connectName]) || !(static::$instances[$connectName] instanceof Queue)) {
|
||||
$connect = ConnectionFactory::getInstance($connectName);
|
||||
if ($connect) {
|
||||
static::$instances[$connectName] = new static($connect);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return static::$instances[$connectName];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置处理程序
|
||||
* @param \Closure $handler
|
||||
*/
|
||||
public function setHandler(\Closure $handler)
|
||||
{
|
||||
$this->connection->setHandler($handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行pop出来的任务(阻塞方法)
|
||||
* @param string $queueName
|
||||
*/
|
||||
public function popRun($queueName)
|
||||
{
|
||||
$this->connection->popRun($queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 入队列
|
||||
* @param Job $job
|
||||
* @param string $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
public function push(Job $job, $queueName)
|
||||
{
|
||||
return $this->connection->push($job, $queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟入队列
|
||||
* @param int $delay 延迟的秒数
|
||||
* @param Job $job
|
||||
* @param string $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
public function later($delay, Job $job, $queueName)
|
||||
{
|
||||
if ($delay <= 0) {
|
||||
return $this->push($job, $queueName);
|
||||
} else {
|
||||
return $this->connection->later($delay, $job, $queueName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 入队列 (对外)
|
||||
* @param JobHandler $handler 回调类
|
||||
* @param String $func 方法名
|
||||
* @param mixed $param 参数
|
||||
* @param String $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
public function pushOn(JobHandler $handler, $func, $param, $queueName)
|
||||
{
|
||||
$job = new Job($handler, $func, $param);
|
||||
return $this->push($job, $queueName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 延迟入队列 (对外)
|
||||
* @param Int $delay 延迟时间/秒
|
||||
* @param JobHandler $handler 回调类
|
||||
* @param String $func 方法名
|
||||
* @param mixed $param 参数
|
||||
* @param String $queueName 队列名
|
||||
* @return boolean
|
||||
*/
|
||||
public function laterOn($delay, JobHandler $handler, $func, $param, $queueName)
|
||||
{
|
||||
$job = new Job($handler, $func, $param);
|
||||
return $this->later($delay, $job, $queueName);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 关闭数据库连接
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$this->connection->close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
*
|
||||
* @date 2021/6/24 15:55
|
||||
*/
|
||||
|
||||
namespace WorkerManQueue;
|
||||
|
||||
|
||||
use Workerman\Timer;
|
||||
use WorkerManQueue\Helpers\LoadConfig;
|
||||
|
||||
class Worker
|
||||
{
|
||||
use LoadConfig;
|
||||
|
||||
protected $config = [];
|
||||
|
||||
/**
|
||||
* Worker constructor.
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function run()
|
||||
{
|
||||
$worker = new \Workerman\Worker();
|
||||
$worker->count = 10;
|
||||
$worker->name = 'queue';
|
||||
|
||||
$queueName = $worker->name;
|
||||
$config = $this->config;
|
||||
|
||||
$worker->onWorkerStart = function () use ($config, $queueName) {
|
||||
Load::Queue($config);
|
||||
$queue = Queue::getInstance();
|
||||
$attempt = 2;
|
||||
|
||||
$queue->setHandler(function (Job $job, $queueName) use ($queue, $attempt) {
|
||||
// 执行任务
|
||||
$job->execute();
|
||||
|
||||
// 判断任务是否执行成功
|
||||
if ($job->isExec()) {
|
||||
//任务成功,触发回调
|
||||
$job->success();
|
||||
} else {
|
||||
// 是否需要重试该任务
|
||||
if ($job->isRetry($attempt)) {
|
||||
// 需要重试,则重新将任务放入队尾
|
||||
$job->reTry($queue, $queueName);
|
||||
} else {
|
||||
// 不需要重试,则任务失败,触发回调
|
||||
$job->failed();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Timer::add(0.1, function () use ($queue, $queueName) {
|
||||
// 消费一次队列任务
|
||||
$queue->popRun($queueName);
|
||||
});
|
||||
};
|
||||
|
||||
\Workerman\Worker::runAll();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user