初始上传
This commit is contained in:
4
vendor/topthink/think-queue/.gitignore
vendored
Executable file
4
vendor/topthink/think-queue/.gitignore
vendored
Executable file
@@ -0,0 +1,4 @@
|
||||
/vendor/
|
||||
/.idea/
|
||||
/composer.lock
|
||||
/thinkphp/
|
||||
201
vendor/topthink/think-queue/LICENSE
vendored
Executable file
201
vendor/topthink/think-queue/LICENSE
vendored
Executable file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
114
vendor/topthink/think-queue/README.md
vendored
Executable file
114
vendor/topthink/think-queue/README.md
vendored
Executable file
@@ -0,0 +1,114 @@
|
||||
# think-queue for ThinkPHP6
|
||||
|
||||
## 安装
|
||||
|
||||
> composer require topthink/think-queue
|
||||
|
||||
## 配置
|
||||
|
||||
> 配置文件位于 `config/queue.php`
|
||||
|
||||
### 公共配置
|
||||
|
||||
```bash
|
||||
[
|
||||
'default'=>'sync' //驱动类型,可选择 sync(默认):同步执行,database:数据库驱动,redis:Redis驱动//或其他自定义的完整的类名
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## 创建任务类
|
||||
> 推荐使用 `app\job` 作为任务类的命名空间
|
||||
> 也可以放在任意可以自动加载到的地方
|
||||
|
||||
任务类不需继承任何类,如果这个类只有一个任务,那么就只需要提供一个`fire`方法就可以了,如果有多个小任务,就写多个方法,下面发布任务的时候会有区别
|
||||
每个方法会传入两个参数 `think\queue\Job $job`(当前的任务对象) 和 `$data`(发布任务时自定义的数据)
|
||||
|
||||
还有个可选的任务失败执行的方法 `failed` 传入的参数为`$data`(发布任务时自定义的数据)
|
||||
|
||||
### 下面写两个例子
|
||||
|
||||
```php
|
||||
namespace app\job;
|
||||
|
||||
use think\queue\Job;
|
||||
|
||||
class Job1{
|
||||
|
||||
public function fire(Job $job, $data){
|
||||
|
||||
//....这里执行具体的任务
|
||||
|
||||
if ($job->attempts() > 3) {
|
||||
//通过这个方法可以检查这个任务已经重试了几次了
|
||||
}
|
||||
|
||||
|
||||
//如果任务执行成功后 记得删除任务,不然这个任务会重复执行,直到达到最大重试次数后失败后,执行failed方法
|
||||
$job->delete();
|
||||
|
||||
// 也可以重新发布这个任务
|
||||
$job->release($delay); //$delay为延迟时间
|
||||
|
||||
}
|
||||
|
||||
public function failed($data){
|
||||
|
||||
// ...任务达到最大重试次数后,失败了
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
```php
|
||||
|
||||
namespace app\lib\job;
|
||||
|
||||
use think\queue\Job;
|
||||
|
||||
class Job2{
|
||||
|
||||
public function task1(Job $job, $data){
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function task2(Job $job, $data){
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function failed($data){
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
## 发布任务
|
||||
> `think\facade\Queue::push($job, $data = '', $queue = null)` 和 `think\facade\Queue::later($delay, $job, $data = '', $queue = null)` 两个方法,前者是立即执行,后者是在`$delay`秒后执行
|
||||
|
||||
`$job` 是任务名
|
||||
命名空间是`app\job`的,比如上面的例子一,写`Job1`类名即可
|
||||
其他的需要些完整的类名,比如上面的例子二,需要写完整的类名`app\lib\job\Job2`
|
||||
如果一个任务类里有多个小任务的话,如上面的例子二,需要用@+方法名`app\lib\job\Job2@task1`、`app\lib\job\Job2@task2`
|
||||
|
||||
`$data` 是你要传到任务里的参数
|
||||
|
||||
`$queue` 队列名,指定这个任务是在哪个队列上执行,同下面监控队列的时候指定的队列名,可不填
|
||||
|
||||
## 监听任务并执行
|
||||
|
||||
```bash
|
||||
&> php think queue:listen
|
||||
|
||||
&> php think queue:work
|
||||
```
|
||||
|
||||
两种,具体的可选参数可以输入命令加 `--help` 查看
|
||||
|
||||
> 可配合supervisor使用,保证进程常驻
|
||||
46
vendor/topthink/think-queue/composer.json
vendored
Executable file
46
vendor/topthink/think-queue/composer.json
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "topthink/think-queue",
|
||||
"description": "The ThinkPHP6 Queue Package",
|
||||
"authors": [
|
||||
{
|
||||
"name": "yunwuxin",
|
||||
"email": "448901948@qq.com"
|
||||
}
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"think\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/common.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"think\\test\\queue\\": "tests"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"topthink/framework": "^6.0",
|
||||
"symfony/process": "^4.2",
|
||||
"nesbot/carbon": "^2.16"
|
||||
},
|
||||
"extra": {
|
||||
"think": {
|
||||
"services": [
|
||||
"think\\queue\\Service"
|
||||
],
|
||||
"config": {
|
||||
"queue": "src/config.php"
|
||||
}
|
||||
}
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.2",
|
||||
"mockery/mockery": "^1.2",
|
||||
"topthink/think-migration": "^3.0.0"
|
||||
}
|
||||
}
|
||||
30
vendor/topthink/think-queue/phpunit.xml.dist
vendored
Executable file
30
vendor/topthink/think-queue/phpunit.xml.dist
vendored
Executable file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnError="false"
|
||||
stopOnFailure="false"
|
||||
verbose="true"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Think Queue Test Suite">
|
||||
<directory suffix="Test.php">./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">./src</directory>
|
||||
<exclude>
|
||||
<file>./src/queue/Service.php</file>
|
||||
<file>./src/common.php</file>
|
||||
<file>./src/config.php</file>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
||||
65
vendor/topthink/think-queue/src/Queue.php
vendored
Executable file
65
vendor/topthink/think-queue/src/Queue.php
vendored
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think;
|
||||
|
||||
use think\queue\Connector;
|
||||
use think\queue\connector\Database;
|
||||
use think\queue\connector\Redis;
|
||||
|
||||
/**
|
||||
* Class Queue
|
||||
* @package think\queue
|
||||
*
|
||||
* @mixin Database
|
||||
* @mixin Redis
|
||||
*/
|
||||
class Queue extends Manager
|
||||
{
|
||||
protected $namespace = '\\think\\queue\\connector\\';
|
||||
|
||||
protected function resolveType(string $name)
|
||||
{
|
||||
return $this->app->config->get("queue.connections.{$name}.type", 'sync');
|
||||
}
|
||||
|
||||
protected function resolveConfig(string $name)
|
||||
{
|
||||
return $this->app->config->get("queue.connections.{$name}");
|
||||
}
|
||||
|
||||
protected function createDriver(string $name)
|
||||
{
|
||||
/** @var Connector $driver */
|
||||
$driver = parent::createDriver($name);
|
||||
|
||||
return $driver->setApp($this->app)
|
||||
->setConnection($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|string $name
|
||||
* @return Connector
|
||||
*/
|
||||
public function connection($name = null)
|
||||
{
|
||||
return $this->driver($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认驱动
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return $this->app->config->get('queue.default');
|
||||
}
|
||||
}
|
||||
31
vendor/topthink/think-queue/src/common.php
vendored
Executable file
31
vendor/topthink/think-queue/src/common.php
vendored
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
use think\facade\Queue;
|
||||
|
||||
if (!function_exists('queue')) {
|
||||
|
||||
/**
|
||||
* 添加到队列
|
||||
* @param $job
|
||||
* @param string $data
|
||||
* @param int $delay
|
||||
* @param null $queue
|
||||
*/
|
||||
function queue($job, $data = '', $delay = 0, $queue = null)
|
||||
{
|
||||
if ($delay > 0) {
|
||||
Queue::later($delay, $job, $data, $queue);
|
||||
} else {
|
||||
Queue::push($job, $data, $queue);
|
||||
}
|
||||
}
|
||||
}
|
||||
39
vendor/topthink/think-queue/src/config.php
vendored
Executable file
39
vendor/topthink/think-queue/src/config.php
vendored
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
return [
|
||||
'default' => 'sync',
|
||||
'connections' => [
|
||||
'sync' => [
|
||||
'type' => 'sync',
|
||||
],
|
||||
'database' => [
|
||||
'type' => 'database',
|
||||
'queue' => 'default',
|
||||
'table' => 'jobs',
|
||||
'connection' => null,
|
||||
],
|
||||
'redis' => [
|
||||
'type' => 'redis',
|
||||
'queue' => 'default',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'password' => '',
|
||||
'select' => 0,
|
||||
'timeout' => 0,
|
||||
'persistent' => false,
|
||||
],
|
||||
],
|
||||
'failed' => [
|
||||
'type' => 'none',
|
||||
'table' => 'failed_jobs',
|
||||
],
|
||||
];
|
||||
18
vendor/topthink/think-queue/src/facade/Queue.php
vendored
Executable file
18
vendor/topthink/think-queue/src/facade/Queue.php
vendored
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace think\facade;
|
||||
|
||||
use think\Facade;
|
||||
|
||||
/**
|
||||
* Class Queue
|
||||
* @package think\facade
|
||||
* @mixin \think\Queue
|
||||
*/
|
||||
class Queue extends Facade
|
||||
{
|
||||
protected static function getFacadeClass()
|
||||
{
|
||||
return \think\Queue::class;
|
||||
}
|
||||
}
|
||||
44
vendor/topthink/think-queue/src/queue/CallQueuedHandler.php
vendored
Executable file
44
vendor/topthink/think-queue/src/queue/CallQueuedHandler.php
vendored
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use think\App;
|
||||
|
||||
class CallQueuedHandler
|
||||
{
|
||||
protected $app;
|
||||
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
public function call(Job $job, array $data)
|
||||
{
|
||||
$command = unserialize($data['command']);
|
||||
|
||||
$this->app->invoke([$command, 'handle']);
|
||||
|
||||
if (!$job->isDeletedOrReleased()) {
|
||||
$job->delete();
|
||||
}
|
||||
}
|
||||
|
||||
public function failed(array $data)
|
||||
{
|
||||
$command = unserialize($data['command']);
|
||||
|
||||
if (method_exists($command, 'failed')) {
|
||||
$command->failed();
|
||||
}
|
||||
}
|
||||
}
|
||||
156
vendor/topthink/think-queue/src/queue/Connector.php
vendored
Executable file
156
vendor/topthink/think-queue/src/queue/Connector.php
vendored
Executable file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
use think\App;
|
||||
|
||||
abstract class Connector
|
||||
{
|
||||
/** @var App */
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* The connector name for the queue.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
protected $options = [];
|
||||
|
||||
abstract public function size($queue = null);
|
||||
|
||||
abstract public function push($job, $data = '', $queue = null);
|
||||
|
||||
public function pushOn($queue, $job, $data = '')
|
||||
{
|
||||
return $this->push($job, $data, $queue);
|
||||
}
|
||||
|
||||
abstract public function pushRaw($payload, $queue = null, array $options = []);
|
||||
|
||||
abstract public function later($delay, $job, $data = '', $queue = null);
|
||||
|
||||
public function laterOn($queue, $delay, $job, $data = '')
|
||||
{
|
||||
return $this->later($delay, $job, $data, $queue);
|
||||
}
|
||||
|
||||
public function bulk($jobs, $data = '', $queue = null)
|
||||
{
|
||||
foreach ((array) $jobs as $job) {
|
||||
$this->push($job, $data, $queue);
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function pop($queue = null);
|
||||
|
||||
protected function createPayload($job, $data = '')
|
||||
{
|
||||
$payload = $this->createPayloadArray($job, $data);
|
||||
|
||||
$payload = json_encode($payload);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
protected function createPayloadArray($job, $data = '')
|
||||
{
|
||||
return is_object($job)
|
||||
? $this->createObjectPayload($job)
|
||||
: $this->createPlainPayload($job, $data);
|
||||
}
|
||||
|
||||
protected function createPlainPayload($job, $data)
|
||||
{
|
||||
return [
|
||||
'job' => $job,
|
||||
'maxTries' => null,
|
||||
'timeout' => null,
|
||||
'data' => $data,
|
||||
];
|
||||
}
|
||||
|
||||
protected function createObjectPayload($job)
|
||||
{
|
||||
return [
|
||||
'job' => 'think\queue\CallQueuedHandler@call',
|
||||
'maxTries' => $job->tries ?? null,
|
||||
'timeout' => $job->timeout ?? null,
|
||||
'timeoutAt' => $this->getJobExpiration($job),
|
||||
'data' => [
|
||||
'commandName' => get_class($job),
|
||||
'command' => serialize(clone $job),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function getJobExpiration($job)
|
||||
{
|
||||
if (!method_exists($job, 'retryUntil') && !isset($job->timeoutAt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$expiration = $job->timeoutAt ?? $job->retryUntil();
|
||||
|
||||
return $expiration instanceof DateTimeInterface
|
||||
? $expiration->getTimestamp() : $expiration;
|
||||
}
|
||||
|
||||
protected function setMeta($payload, $key, $value)
|
||||
{
|
||||
$payload = json_decode($payload, true);
|
||||
$payload[$key] = $value;
|
||||
$payload = json_encode($payload);
|
||||
|
||||
if (JSON_ERROR_NONE !== json_last_error()) {
|
||||
throw new InvalidArgumentException('Unable to create payload: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function setApp(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connector name for the queue.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connector name for the queue.
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setConnection($name)
|
||||
{
|
||||
$this->connection = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
47
vendor/topthink/think-queue/src/queue/FailedJob.php
vendored
Executable file
47
vendor/topthink/think-queue/src/queue/FailedJob.php
vendored
Executable file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
abstract class FailedJob
|
||||
{
|
||||
/**
|
||||
* Log a failed job into storage.
|
||||
*
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param string $payload
|
||||
* @param \Exception $exception
|
||||
* @return int|null
|
||||
*/
|
||||
abstract public function log($connection, $queue, $payload, $exception);
|
||||
|
||||
/**
|
||||
* Get a list of all of the failed jobs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function all();
|
||||
|
||||
/**
|
||||
* Get a single failed job.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return object|null
|
||||
*/
|
||||
abstract public function find($id);
|
||||
|
||||
/**
|
||||
* Delete a single failed job from storage.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function forget($id);
|
||||
|
||||
/**
|
||||
* Flush all of the failed jobs from storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function flush();
|
||||
}
|
||||
65
vendor/topthink/think-queue/src/queue/InteractsWithTime.php
vendored
Executable file
65
vendor/topthink/think-queue/src/queue/InteractsWithTime.php
vendored
Executable file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
|
||||
trait InteractsWithTime
|
||||
{
|
||||
/**
|
||||
* Get the number of seconds until the given DateTime.
|
||||
*
|
||||
* @param DateTimeInterface|DateInterval|int $delay
|
||||
* @return int
|
||||
*/
|
||||
protected function secondsUntil($delay)
|
||||
{
|
||||
$delay = $this->parseDateInterval($delay);
|
||||
|
||||
return $delay instanceof DateTimeInterface
|
||||
? max(0, $delay->getTimestamp() - $this->currentTime())
|
||||
: (int) $delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the "available at" UNIX timestamp.
|
||||
*
|
||||
* @param DateTimeInterface|DateInterval|int $delay
|
||||
* @return int
|
||||
*/
|
||||
protected function availableAt($delay = 0)
|
||||
{
|
||||
$delay = $this->parseDateInterval($delay);
|
||||
|
||||
return $delay instanceof DateTimeInterface
|
||||
? $delay->getTimestamp()
|
||||
: Carbon::now()->addRealSeconds($delay)->getTimestamp();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the given value is an interval, convert it to a DateTime instance.
|
||||
*
|
||||
* @param DateTimeInterface|DateInterval|int $delay
|
||||
* @return DateTimeInterface|int
|
||||
*/
|
||||
protected function parseDateInterval($delay)
|
||||
{
|
||||
if ($delay instanceof DateInterval) {
|
||||
$delay = Carbon::now()->add($delay);
|
||||
}
|
||||
|
||||
return $delay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current system time as a UNIX timestamp.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function currentTime()
|
||||
{
|
||||
return Carbon::now()->getTimestamp();
|
||||
}
|
||||
}
|
||||
305
vendor/topthink/think-queue/src/queue/Job.php
vendored
Executable file
305
vendor/topthink/think-queue/src/queue/Job.php
vendored
Executable file
@@ -0,0 +1,305 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use Exception;
|
||||
use think\App;
|
||||
use think\helper\Arr;
|
||||
use think\helper\Str;
|
||||
|
||||
abstract class Job
|
||||
{
|
||||
|
||||
/**
|
||||
* The job handler instance.
|
||||
* @var object
|
||||
*/
|
||||
private $instance;
|
||||
|
||||
/**
|
||||
* The JSON decoded version of "$job".
|
||||
* @var array
|
||||
*/
|
||||
private $payload;
|
||||
|
||||
/**
|
||||
* @var App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* The name of the queue the job belongs to.
|
||||
* @var string
|
||||
*/
|
||||
protected $queue;
|
||||
|
||||
/**
|
||||
* The name of the connection the job belongs to.
|
||||
*/
|
||||
protected $connection;
|
||||
|
||||
/**
|
||||
* Indicates if the job has been deleted.
|
||||
* @var bool
|
||||
*/
|
||||
protected $deleted = false;
|
||||
|
||||
/**
|
||||
* Indicates if the job has been released.
|
||||
* @var bool
|
||||
*/
|
||||
protected $released = false;
|
||||
|
||||
/**
|
||||
* Indicates if the job has failed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $failed = false;
|
||||
|
||||
/**
|
||||
* Get the decoded body of the job.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function payload($name = null, $default = null)
|
||||
{
|
||||
if (empty($this->payload)) {
|
||||
$this->payload = json_decode($this->getRawBody(), true);
|
||||
}
|
||||
if (empty($name)) {
|
||||
return $this->payload;
|
||||
}
|
||||
return Arr::get($this->payload, $name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire the job.
|
||||
* @return void
|
||||
*/
|
||||
public function fire()
|
||||
{
|
||||
$instance = $this->getResolvedJob();
|
||||
|
||||
[, $method] = $this->getParsedJob();
|
||||
|
||||
$instance->{$method}($this, $this->payload('data'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Process an exception that caused the job to fail.
|
||||
*
|
||||
* @param Exception $e
|
||||
* @return void
|
||||
*/
|
||||
public function failed($e)
|
||||
{
|
||||
$instance = $this->getResolvedJob();
|
||||
|
||||
if (method_exists($instance, 'failed')) {
|
||||
$instance->failed($this->payload('data'), $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the job from the queue.
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->deleted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the job has been deleted.
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeleted()
|
||||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Release the job back into the queue.
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
public function release($delay = 0)
|
||||
{
|
||||
$this->released = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the job was released back into the queue.
|
||||
* @return bool
|
||||
*/
|
||||
public function isReleased()
|
||||
{
|
||||
return $this->released;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the job has been deleted or released.
|
||||
* @return bool
|
||||
*/
|
||||
public function isDeletedOrReleased()
|
||||
{
|
||||
return $this->isDeleted() || $this->isReleased();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getJobId();
|
||||
|
||||
/**
|
||||
* Get the number of times the job has been attempted.
|
||||
* @return int
|
||||
*/
|
||||
abstract public function attempts();
|
||||
|
||||
/**
|
||||
* Get the raw body string for the job.
|
||||
* @return string
|
||||
*/
|
||||
abstract public function getRawBody();
|
||||
|
||||
/**
|
||||
* Parse the job declaration into class and method.
|
||||
* @return array
|
||||
*/
|
||||
protected function getParsedJob()
|
||||
{
|
||||
$job = $this->payload('job');
|
||||
$segments = explode('@', $job);
|
||||
|
||||
return count($segments) > 1 ? $segments : [$segments[0], 'fire'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the given job handler.
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
protected function resolve($name, $param)
|
||||
{
|
||||
// $namespace = $this->app->getNamespace() . '\\job\\';
|
||||
////
|
||||
//// $class = false !== strpos($name, '\\') ? $name : $namespace . Str::studly($name);
|
||||
////
|
||||
//// return $this->app->make($class, [$param], true);
|
||||
if (strpos($name, '\\') === false) {
|
||||
|
||||
if (strpos($name, '/') === false) {
|
||||
$app = '';
|
||||
} else {
|
||||
list($app, $name) = explode('/', $name, 2);
|
||||
}
|
||||
|
||||
$name = ($this->app->config->get('app.app_namespace') ?: 'app\\') . ($app ? strtolower($app) . '\\' : '') . 'job\\' . $name;
|
||||
}
|
||||
|
||||
return $this->app->make($name, [$param], true);
|
||||
}
|
||||
|
||||
public function getResolvedJob()
|
||||
{
|
||||
if (empty($this->instance)) {
|
||||
[$class] = $this->getParsedJob();
|
||||
|
||||
$this->instance = $this->resolve($class, $this->payload('data'));
|
||||
}
|
||||
|
||||
return $this->instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the job has been marked as a failure.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasFailed()
|
||||
{
|
||||
return $this->failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark the job as "failed".
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markAsFailed()
|
||||
{
|
||||
$this->failed = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of times to attempt a job.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function maxTries()
|
||||
{
|
||||
return $this->payload('maxTries');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds the job can run.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function timeout()
|
||||
{
|
||||
return $this->payload('timeout');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timestamp indicating when the job should timeout.
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function timeoutAt()
|
||||
{
|
||||
return $this->payload('timeoutAt');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the queued job class.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->payload('job');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the connection the job belongs to.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of the queue the job belongs to.
|
||||
* @return string
|
||||
*/
|
||||
public function getQueue()
|
||||
{
|
||||
return $this->queue;
|
||||
}
|
||||
}
|
||||
162
vendor/topthink/think-queue/src/queue/Listener.php
vendored
Executable file
162
vendor/topthink/think-queue/src/queue/Listener.php
vendored
Executable file
@@ -0,0 +1,162 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use Closure;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
use think\App;
|
||||
|
||||
class Listener
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $commandPath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $workerCommand;
|
||||
|
||||
/**
|
||||
* @var \Closure|null
|
||||
*/
|
||||
protected $outputHandler;
|
||||
|
||||
/**
|
||||
* @param string $commandPath
|
||||
*/
|
||||
public function __construct($commandPath)
|
||||
{
|
||||
$this->commandPath = $commandPath;
|
||||
}
|
||||
|
||||
public static function __make(App $app)
|
||||
{
|
||||
return new self($app->getRootPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PHP binary.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function phpBinary()
|
||||
{
|
||||
return (new PhpExecutableFinder)->find(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param int $delay
|
||||
* @param int $sleep
|
||||
* @param int $maxTries
|
||||
* @param int $memory
|
||||
* @param int $timeout
|
||||
* @return void
|
||||
*/
|
||||
public function listen($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0, $memory = 128, $timeout = 60)
|
||||
{
|
||||
$process = $this->makeProcess($connection, $queue, $delay, $sleep, $maxTries, $memory, $timeout);
|
||||
|
||||
while (true) {
|
||||
$this->runProcess($process, $memory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param int $delay
|
||||
* @param int $sleep
|
||||
* @param int $maxTries
|
||||
* @param int $memory
|
||||
* @param int $timeout
|
||||
* @return Process
|
||||
*/
|
||||
public function makeProcess($connection, $queue, $delay, $sleep, $maxTries, $memory, $timeout)
|
||||
{
|
||||
$command = array_filter([
|
||||
$this->phpBinary(),
|
||||
'think',
|
||||
'queue:work',
|
||||
$connection,
|
||||
'--once',
|
||||
"--queue={$queue}",
|
||||
"--delay={$delay}",
|
||||
"--memory={$memory}",
|
||||
"--sleep={$sleep}",
|
||||
"--tries={$maxTries}",
|
||||
], function ($value) {
|
||||
return !is_null($value);
|
||||
});
|
||||
|
||||
return new Process($command, $this->commandPath, null, null, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Process $process
|
||||
* @param int $memory
|
||||
*/
|
||||
public function runProcess(Process $process, $memory)
|
||||
{
|
||||
$process->run(function ($type, $line) {
|
||||
$this->handleWorkerOutput($type, $line);
|
||||
});
|
||||
|
||||
if ($this->memoryExceeded($memory)) {
|
||||
$this->stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
* @param string $line
|
||||
* @return void
|
||||
*/
|
||||
protected function handleWorkerOutput($type, $line)
|
||||
{
|
||||
if (isset($this->outputHandler)) {
|
||||
call_user_func($this->outputHandler, $type, $line);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $memoryLimit
|
||||
* @return bool
|
||||
*/
|
||||
public function memoryExceeded($memoryLimit)
|
||||
{
|
||||
return (memory_get_usage() / 1024 / 1024) >= $memoryLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function stop()
|
||||
{
|
||||
die;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Closure $outputHandler
|
||||
* @return void
|
||||
*/
|
||||
public function setOutputHandler(Closure $outputHandler)
|
||||
{
|
||||
$this->outputHandler = $outputHandler;
|
||||
}
|
||||
|
||||
}
|
||||
61
vendor/topthink/think-queue/src/queue/Queueable.php
vendored
Executable file
61
vendor/topthink/think-queue/src/queue/Queueable.php
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
trait Queueable
|
||||
{
|
||||
|
||||
/** @var string 连接 */
|
||||
public $connection;
|
||||
|
||||
/** @var string 队列名称 */
|
||||
public $queue;
|
||||
|
||||
/** @var integer 延迟时间 */
|
||||
public $delay;
|
||||
|
||||
/**
|
||||
* 设置连接名
|
||||
* @param $connection
|
||||
* @return $this
|
||||
*/
|
||||
public function onConnection($connection)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置队列名
|
||||
* @param $queue
|
||||
* @return $this
|
||||
*/
|
||||
public function onQueue($queue)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置延迟时间
|
||||
* @param $delay
|
||||
* @return $this
|
||||
*/
|
||||
public function delay($delay)
|
||||
{
|
||||
$this->delay = $delay;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
50
vendor/topthink/think-queue/src/queue/Service.php
vendored
Executable file
50
vendor/topthink/think-queue/src/queue/Service.php
vendored
Executable file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use think\helper\Arr;
|
||||
use think\helper\Str;
|
||||
use think\Queue;
|
||||
use think\queue\command\FailedTable;
|
||||
use think\queue\command\FlushFailed;
|
||||
use think\queue\command\ForgetFailed;
|
||||
use think\queue\command\Listen;
|
||||
use think\queue\command\ListFailed;
|
||||
use think\queue\command\Restart;
|
||||
use think\queue\command\Retry;
|
||||
use think\queue\command\Table;
|
||||
use think\queue\command\Work;
|
||||
|
||||
class Service extends \think\Service
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
$this->app->bind('queue', Queue::class);
|
||||
$this->app->bind('queue.failer', function () {
|
||||
|
||||
$config = $this->app->config->get('queue.failed', []);
|
||||
|
||||
$type = Arr::pull($config, 'type', 'none');
|
||||
|
||||
$class = false !== strpos($type, '\\') ? $type : '\\think\\queue\\failed\\' . Str::studly($type);
|
||||
|
||||
return $this->app->invokeClass($class, [$config]);
|
||||
});
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
$this->commands([
|
||||
FailedJob::class,
|
||||
Table::class,
|
||||
FlushFailed::class,
|
||||
ForgetFailed::class,
|
||||
ListFailed::class,
|
||||
Retry::class,
|
||||
Work::class,
|
||||
Restart::class,
|
||||
Listen::class,
|
||||
FailedTable::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
17
vendor/topthink/think-queue/src/queue/ShouldQueue.php
vendored
Executable file
17
vendor/topthink/think-queue/src/queue/ShouldQueue.php
vendored
Executable file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2016 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
interface ShouldQueue
|
||||
{
|
||||
|
||||
}
|
||||
419
vendor/topthink/think-queue/src/queue/Worker.php
vendored
Executable file
419
vendor/topthink/think-queue/src/queue/Worker.php
vendored
Executable file
@@ -0,0 +1,419 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use RuntimeException;
|
||||
use think\Cache;
|
||||
use think\Event;
|
||||
use think\exception\Handle;
|
||||
use think\Queue;
|
||||
use think\queue\event\JobExceptionOccurred;
|
||||
use think\queue\event\JobFailed;
|
||||
use think\queue\event\JobProcessed;
|
||||
use think\queue\event\JobProcessing;
|
||||
use think\queue\event\WorkerStopping;
|
||||
use think\queue\exception\MaxAttemptsExceededException;
|
||||
use Throwable;
|
||||
|
||||
class Worker
|
||||
{
|
||||
/** @var Event */
|
||||
protected $event;
|
||||
/** @var Handle */
|
||||
protected $handle;
|
||||
/** @var Queue */
|
||||
protected $queue;
|
||||
|
||||
/** @var Cache */
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Indicates if the worker should exit.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $shouldQuit = false;
|
||||
|
||||
/**
|
||||
* Indicates if the worker is paused.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $paused = false;
|
||||
|
||||
public function __construct(Queue $queue, Event $event, Handle $handle, Cache $cache = null)
|
||||
{
|
||||
$this->queue = $queue;
|
||||
$this->event = $event;
|
||||
$this->handle = $handle;
|
||||
$this->cache = $cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param int $delay
|
||||
* @param int $sleep
|
||||
* @param int $maxTries
|
||||
* @param int $memory
|
||||
* @param int $timeout
|
||||
*/
|
||||
public function daemon($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0, $memory = 128, $timeout = 60)
|
||||
{
|
||||
if ($this->supportsAsyncSignals()) {
|
||||
$this->listenForSignals();
|
||||
}
|
||||
|
||||
$lastRestart = $this->getTimestampOfLastQueueRestart();
|
||||
|
||||
while (true) {
|
||||
|
||||
$job = $this->getNextJob(
|
||||
$this->queue->connection($connection),
|
||||
$queue
|
||||
);
|
||||
|
||||
if ($this->supportsAsyncSignals()) {
|
||||
$this->registerTimeoutHandler($job, $timeout);
|
||||
}
|
||||
|
||||
if ($job) {
|
||||
$this->runJob($job, $connection, $maxTries, $delay);
|
||||
} else {
|
||||
$this->sleep($sleep);
|
||||
}
|
||||
|
||||
$this->stopIfNecessary($job, $lastRestart, $memory);
|
||||
}
|
||||
}
|
||||
|
||||
protected function stopIfNecessary($job, $lastRestart, $memory)
|
||||
{
|
||||
if ($this->shouldQuit || $this->queueShouldRestart($lastRestart)) {
|
||||
$this->stop();
|
||||
} elseif ($this->memoryExceeded($memory)) {
|
||||
$this->stop(12);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the queue worker should restart.
|
||||
*
|
||||
* @param int|null $lastRestart
|
||||
* @return bool
|
||||
*/
|
||||
protected function queueShouldRestart($lastRestart)
|
||||
{
|
||||
return $this->getTimestampOfLastQueueRestart() != $lastRestart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the memory limit has been exceeded.
|
||||
*
|
||||
* @param int $memoryLimit
|
||||
* @return bool
|
||||
*/
|
||||
public function memoryExceeded($memoryLimit)
|
||||
{
|
||||
return (memory_get_usage(true) / 1024 / 1024) >= $memoryLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列重启时间
|
||||
* @return mixed
|
||||
*/
|
||||
protected function getTimestampOfLastQueueRestart()
|
||||
{
|
||||
if ($this->cache) {
|
||||
return $this->cache->get('think:queue:restart');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the worker timeout handler.
|
||||
*
|
||||
* @param Job|null $job
|
||||
* @param int $timeout
|
||||
* @return void
|
||||
*/
|
||||
protected function registerTimeoutHandler($job, $timeout)
|
||||
{
|
||||
pcntl_signal(SIGALRM, function () {
|
||||
$this->kill(1);
|
||||
});
|
||||
|
||||
pcntl_alarm(
|
||||
max($this->timeoutForJob($job, $timeout), 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening and bail out of the script.
|
||||
*
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
public function stop($status = 0)
|
||||
{
|
||||
$this->event->trigger(new WorkerStopping($status));
|
||||
|
||||
exit($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Kill the process.
|
||||
*
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
public function kill($status = 0)
|
||||
{
|
||||
$this->event->trigger(new WorkerStopping($status));
|
||||
|
||||
if (extension_loaded('posix')) {
|
||||
posix_kill(getmypid(), SIGKILL);
|
||||
}
|
||||
|
||||
exit($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate timeout for the given job.
|
||||
*
|
||||
* @param Job|null $job
|
||||
* @param int $timeout
|
||||
* @return int
|
||||
*/
|
||||
protected function timeoutForJob($job, $timeout)
|
||||
{
|
||||
return $job && !is_null($job->timeout()) ? $job->timeout() : $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if "async" signals are supported.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function supportsAsyncSignals()
|
||||
{
|
||||
return extension_loaded('pcntl');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable async signals for the process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function listenForSignals()
|
||||
{
|
||||
pcntl_async_signals(true);
|
||||
|
||||
pcntl_signal(SIGTERM, function () {
|
||||
$this->shouldQuit = true;
|
||||
});
|
||||
|
||||
pcntl_signal(SIGUSR2, function () {
|
||||
$this->paused = true;
|
||||
});
|
||||
|
||||
pcntl_signal(SIGCONT, function () {
|
||||
$this->paused = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行下个任务
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param int $delay
|
||||
* @param int $sleep
|
||||
* @param int $maxTries
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function runNextJob($connection, $queue, $delay = 0, $sleep = 3, $maxTries = 0)
|
||||
{
|
||||
|
||||
$job = $this->getNextJob($this->queue->connection($connection), $queue);
|
||||
|
||||
if ($job) {
|
||||
$this->runJob($job, $connection, $maxTries, $delay);
|
||||
} else {
|
||||
$this->sleep($sleep);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务
|
||||
* @param Job $job
|
||||
* @param string $connection
|
||||
* @param int $maxTries
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
protected function runJob($job, $connection, $maxTries, $delay)
|
||||
{
|
||||
try {
|
||||
$this->process($connection, $job, $maxTries, $delay);
|
||||
} catch (Exception | Throwable $e) {
|
||||
$this->handle->report($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下个任务
|
||||
* @param Connector $connector
|
||||
* @param string $queue
|
||||
* @return Job
|
||||
*/
|
||||
protected function getNextJob($connector, $queue)
|
||||
{
|
||||
try {
|
||||
foreach (explode(',', $queue) as $queue) {
|
||||
if (!is_null($job = $connector->pop($queue))) {
|
||||
return $job;
|
||||
}
|
||||
}
|
||||
} catch (Exception | Throwable $e) {
|
||||
$this->handle->report($e);
|
||||
$this->sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a given job from the queue.
|
||||
* @param string $connection
|
||||
* @param Job $job
|
||||
* @param int $maxTries
|
||||
* @param int $delay
|
||||
* @return void
|
||||
* @throws Exception
|
||||
*/
|
||||
public function process($connection, $job, $maxTries = 0, $delay = 0)
|
||||
{
|
||||
try {
|
||||
$this->event->trigger(new JobProcessing($connection, $job));
|
||||
|
||||
$this->markJobAsFailedIfAlreadyExceedsMaxAttempts(
|
||||
$connection,
|
||||
$job,
|
||||
(int) $maxTries
|
||||
);
|
||||
|
||||
$job->fire();
|
||||
|
||||
$this->event->trigger(new JobProcessed($connection, $job));
|
||||
} catch (Exception | Throwable $e) {
|
||||
try {
|
||||
if (!$job->hasFailed()) {
|
||||
$this->markJobAsFailedIfWillExceedMaxAttempts($connection, $job, (int) $maxTries, $e);
|
||||
}
|
||||
|
||||
$this->event->trigger(new JobExceptionOccurred($connection, $job, $e));
|
||||
} finally {
|
||||
if (!$job->isDeleted() && !$job->isReleased() && !$job->hasFailed()) {
|
||||
$job->release($delay);
|
||||
}
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param Job $job
|
||||
* @param int $maxTries
|
||||
*/
|
||||
protected function markJobAsFailedIfAlreadyExceedsMaxAttempts($connection, $job, $maxTries)
|
||||
{
|
||||
$maxTries = !is_null($job->maxTries()) ? $job->maxTries() : $maxTries;
|
||||
|
||||
$timeoutAt = $job->timeoutAt();
|
||||
|
||||
if ($timeoutAt && Carbon::now()->getTimestamp() <= $timeoutAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$timeoutAt && (0 === $maxTries || $job->attempts() <= $maxTries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->failJob($connection, $job, $e = new MaxAttemptsExceededException(
|
||||
$job->getName() . ' has been attempted too many times or run too long. The job may have previously timed out.'
|
||||
));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param Job $job
|
||||
* @param int $maxTries
|
||||
* @param Exception $e
|
||||
*/
|
||||
protected function markJobAsFailedIfWillExceedMaxAttempts($connection, $job, $maxTries, $e)
|
||||
{
|
||||
$maxTries = !is_null($job->maxTries()) ? $job->maxTries() : $maxTries;
|
||||
|
||||
if ($job->timeoutAt() && $job->timeoutAt() <= Carbon::now()->getTimestamp()) {
|
||||
$this->failJob($connection, $job, $e);
|
||||
}
|
||||
|
||||
if ($maxTries > 0 && $job->attempts() >= $maxTries) {
|
||||
$this->failJob($connection, $job, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $connection
|
||||
* @param Job $job
|
||||
* @param Exception $e
|
||||
*/
|
||||
protected function failJob($connection, $job, $e)
|
||||
{
|
||||
$job->markAsFailed();
|
||||
|
||||
if ($job->isDeleted()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
$job->delete();
|
||||
|
||||
$job->failed($e);
|
||||
} finally {
|
||||
$this->event->trigger(new JobFailed(
|
||||
$connection,
|
||||
$job,
|
||||
$e ?: new RuntimeException('ManuallyFailed')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleep the script for a given number of seconds.
|
||||
* @param int $seconds
|
||||
* @return void
|
||||
*/
|
||||
public function sleep($seconds)
|
||||
{
|
||||
if ($seconds < 1) {
|
||||
usleep($seconds * 1000000);
|
||||
} else {
|
||||
sleep($seconds);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
46
vendor/topthink/think-queue/src/queue/command/FailedTable.php
vendored
Executable file
46
vendor/topthink/think-queue/src/queue/command/FailedTable.php
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\helper\Str;
|
||||
use think\migration\Creator;
|
||||
|
||||
class FailedTable extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:failed-table')
|
||||
->setDescription('Create a migration for the failed queue jobs database table');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->app->has('migration.creator')) {
|
||||
$this->output->error('Install think-migration first please');
|
||||
return;
|
||||
}
|
||||
|
||||
$table = $this->app->config->get('queue.failed.table');
|
||||
|
||||
$className = Str::studly("create_{$table}_table");
|
||||
|
||||
/** @var Creator $creator */
|
||||
$creator = $this->app->get('migration.creator');
|
||||
|
||||
$path = $creator->create($className);
|
||||
|
||||
// Load the alternative template if it is defined.
|
||||
$contents = file_get_contents(__DIR__ . '/stubs/failed_jobs.stub');
|
||||
|
||||
// inject the class names appropriate to this migration
|
||||
$contents = strtr($contents, [
|
||||
'CreateFailedJobsTable' => $className,
|
||||
'{{table}}' => $table,
|
||||
]);
|
||||
|
||||
file_put_contents($path, $contents);
|
||||
|
||||
$this->output->info('Migration created successfully!');
|
||||
}
|
||||
}
|
||||
21
vendor/topthink/think-queue/src/queue/command/FlushFailed.php
vendored
Executable file
21
vendor/topthink/think-queue/src/queue/command/FlushFailed.php
vendored
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
|
||||
class FlushFailed extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:flush')
|
||||
->setDescription('Flush all of the failed queue jobs');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$this->app->get('queue.failer')->flush();
|
||||
|
||||
$this->output->info('All failed jobs deleted successfully!');
|
||||
}
|
||||
}
|
||||
25
vendor/topthink/think-queue/src/queue/command/ForgetFailed.php
vendored
Executable file
25
vendor/topthink/think-queue/src/queue/command/ForgetFailed.php
vendored
Executable file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\input\Argument;
|
||||
|
||||
class ForgetFailed extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:forget')
|
||||
->addArgument('id', Argument::REQUIRED, 'The ID of the failed job')
|
||||
->setDescription('Delete a failed queue job');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if ($this->app['queue.failer']->forget($this->input->getArgument('id'))) {
|
||||
$this->output->info('Failed job deleted successfully!');
|
||||
} else {
|
||||
$this->output->error('No failed job matches the given ID.');
|
||||
}
|
||||
}
|
||||
}
|
||||
110
vendor/topthink/think-queue/src/queue/command/ListFailed.php
vendored
Executable file
110
vendor/topthink/think-queue/src/queue/command/ListFailed.php
vendored
Executable file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Table;
|
||||
use think\helper\Arr;
|
||||
|
||||
class ListFailed extends Command
|
||||
{
|
||||
/**
|
||||
* The table headers for the command.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = ['ID', 'Connection', 'Queue', 'Class', 'Fail Time'];
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:failed')
|
||||
->setDescription('List all of the failed queue jobs');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (count($jobs = $this->getFailedJobs()) === 0) {
|
||||
$this->output->info('No failed jobs!');
|
||||
return;
|
||||
}
|
||||
$this->displayFailedJobs($jobs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the failed jobs in the console.
|
||||
*
|
||||
* @param array $jobs
|
||||
* @return void
|
||||
*/
|
||||
protected function displayFailedJobs(array $jobs)
|
||||
{
|
||||
$table = new Table();
|
||||
$table->setHeader($this->headers);
|
||||
$table->setRows($jobs);
|
||||
|
||||
$this->table($table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile the failed jobs into a displayable format.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getFailedJobs()
|
||||
{
|
||||
$failed = $this->app['queue.failer']->all();
|
||||
|
||||
return collect($failed)->map(function ($failed) {
|
||||
return $this->parseFailedJob((array) $failed);
|
||||
})->filter()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the failed job row.
|
||||
*
|
||||
* @param array $failed
|
||||
* @return array
|
||||
*/
|
||||
protected function parseFailedJob(array $failed)
|
||||
{
|
||||
$row = array_values(Arr::except($failed, ['payload', 'exception']));
|
||||
|
||||
array_splice($row, 3, 0, $this->extractJobName($failed['payload']));
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the failed job name from payload.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return string|null
|
||||
*/
|
||||
private function extractJobName($payload)
|
||||
{
|
||||
$payload = json_decode($payload, true);
|
||||
|
||||
if ($payload && (!isset($payload['data']['command']))) {
|
||||
return $payload['job'] ?? null;
|
||||
} elseif ($payload && isset($payload['data']['command'])) {
|
||||
return $this->matchJobName($payload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the job name from the payload.
|
||||
*
|
||||
* @param array $payload
|
||||
* @return string
|
||||
*/
|
||||
protected function matchJobName($payload)
|
||||
{
|
||||
preg_match('/"([^"]+)"/', $payload['data']['command'], $matches);
|
||||
|
||||
if (isset($matches[1])) {
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return $payload['job'] ?? null;
|
||||
}
|
||||
}
|
||||
61
vendor/topthink/think-queue/src/queue/command/Listen.php
vendored
Executable file
61
vendor/topthink/think-queue/src/queue/command/Listen.php
vendored
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\queue\Listener;
|
||||
|
||||
class Listen extends Command
|
||||
{
|
||||
/** @var Listener */
|
||||
protected $listener;
|
||||
|
||||
public function __construct(Listener $listener)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->listener = $listener;
|
||||
$this->listener->setOutputHandler(function ($type, $line) {
|
||||
$this->output->write($line);
|
||||
});
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:listen')
|
||||
->addArgument('connection', Argument::OPTIONAL, 'The name of the queue connection to work', null)
|
||||
->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on', null)
|
||||
->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0)
|
||||
->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128)
|
||||
->addOption('timeout', null, Option::VALUE_OPTIONAL, 'Seconds a job may run before timing out', 60)
|
||||
->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Seconds to wait before checking queue for jobs', 3)
|
||||
->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0)
|
||||
->setDescription('Listen to a given queue');
|
||||
}
|
||||
|
||||
public function execute(Input $input, Output $output)
|
||||
{
|
||||
$connection = $input->getArgument('connection') ?: $this->app->config->get('queue.default');
|
||||
|
||||
$queue = $input->getOption('queue') ?: $this->app->config->get("queue.connections.{$connection}.queue", 'default');
|
||||
$delay = $input->getOption('delay');
|
||||
$memory = $input->getOption('memory');
|
||||
$timeout = $input->getOption('timeout');
|
||||
$sleep = $input->getOption('sleep');
|
||||
$tries = $input->getOption('tries');
|
||||
|
||||
$this->listener->listen($connection, $queue, $delay, $sleep, $tries, $memory, $timeout);
|
||||
}
|
||||
}
|
||||
33
vendor/topthink/think-queue/src/queue/command/Restart.php
vendored
Executable file
33
vendor/topthink/think-queue/src/queue/command/Restart.php
vendored
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\Cache;
|
||||
use think\console\Command;
|
||||
use think\queue\InteractsWithTime;
|
||||
|
||||
class Restart extends Command
|
||||
{
|
||||
use InteractsWithTime;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:restart')
|
||||
->setDescription('Restart queue worker daemons after their current job');
|
||||
}
|
||||
|
||||
public function handle(Cache $cache)
|
||||
{
|
||||
$cache->set('think:queue:restart', $this->currentTime());
|
||||
$this->output->info("Broadcasting queue restart signal.");
|
||||
}
|
||||
}
|
||||
84
vendor/topthink/think-queue/src/queue/command/Retry.php
vendored
Executable file
84
vendor/topthink/think-queue/src/queue/command/Retry.php
vendored
Executable file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use stdClass;
|
||||
use think\console\Command;
|
||||
use think\console\input\Argument;
|
||||
use think\helper\Arr;
|
||||
|
||||
class Retry extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:retry')
|
||||
->addArgument('id', Argument::IS_ARRAY | Argument::REQUIRED, 'The ID of the failed job or "all" to retry all jobs')
|
||||
->setDescription('Retry a failed queue job');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
foreach ($this->getJobIds() as $id) {
|
||||
$job = $this->app['queue.failer']->find($id);
|
||||
|
||||
if (is_null($job)) {
|
||||
$this->output->error("Unable to find failed job with ID [{$id}].");
|
||||
} else {
|
||||
$this->retryJob($job);
|
||||
|
||||
$this->output->info("The failed job [{$id}] has been pushed back onto the queue!");
|
||||
|
||||
$this->app['queue.failer']->forget($id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retry the queue job.
|
||||
*
|
||||
* @param stdClass $job
|
||||
* @return void
|
||||
*/
|
||||
protected function retryJob($job)
|
||||
{
|
||||
$this->app['queue']->connection($job['connection'])->pushRaw(
|
||||
$this->resetAttempts($job['payload']),
|
||||
$job['queue']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the payload attempts.
|
||||
*
|
||||
* Applicable to Redis jobs which store attempts in their payload.
|
||||
*
|
||||
* @param string $payload
|
||||
* @return string
|
||||
*/
|
||||
protected function resetAttempts($payload)
|
||||
{
|
||||
$payload = json_decode($payload, true);
|
||||
|
||||
if (isset($payload['attempts'])) {
|
||||
$payload['attempts'] = 0;
|
||||
}
|
||||
|
||||
return json_encode($payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job IDs to be retried.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getJobIds()
|
||||
{
|
||||
$ids = (array) $this->input->getArgument('id');
|
||||
|
||||
if (count($ids) === 1 && $ids[0] === 'all') {
|
||||
$ids = Arr::pluck($this->app['queue.failer']->all(), 'id');
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
}
|
||||
46
vendor/topthink/think-queue/src/queue/command/Table.php
vendored
Executable file
46
vendor/topthink/think-queue/src/queue/command/Table.php
vendored
Executable file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\helper\Str;
|
||||
use think\migration\Creator;
|
||||
|
||||
class Table extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:table')
|
||||
->setDescription('Create a migration for the queue jobs database table');
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->app->has('migration.creator')) {
|
||||
$this->output->error('Install think-migration first please');
|
||||
return;
|
||||
}
|
||||
|
||||
$table = $this->app->config->get('queue.connections.database.table');
|
||||
|
||||
$className = Str::studly("create_{$table}_table");
|
||||
|
||||
/** @var Creator $creator */
|
||||
$creator = $this->app->get('migration.creator');
|
||||
|
||||
$path = $creator->create($className);
|
||||
|
||||
// Load the alternative template if it is defined.
|
||||
$contents = file_get_contents(__DIR__ . '/stubs/jobs.stub');
|
||||
|
||||
// inject the class names appropriate to this migration
|
||||
$contents = strtr($contents, [
|
||||
'CreateJobsTable' => $className,
|
||||
'{{table}}' => $table,
|
||||
]);
|
||||
|
||||
file_put_contents($path, $contents);
|
||||
|
||||
$this->output->info('Migration created successfully!');
|
||||
}
|
||||
}
|
||||
154
vendor/topthink/think-queue/src/queue/command/Work.php
vendored
Executable file
154
vendor/topthink/think-queue/src/queue/command/Work.php
vendored
Executable file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\queue\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\input\Argument;
|
||||
use think\console\input\Option;
|
||||
use think\console\Output;
|
||||
use think\queue\event\JobFailed;
|
||||
use think\queue\event\JobProcessed;
|
||||
use think\queue\event\JobProcessing;
|
||||
use think\queue\Job;
|
||||
use think\queue\Worker;
|
||||
|
||||
class Work extends Command
|
||||
{
|
||||
|
||||
/**
|
||||
* The queue worker instance.
|
||||
* @var Worker
|
||||
*/
|
||||
protected $worker;
|
||||
|
||||
public function __construct(Worker $worker)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->worker = $worker;
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('queue:work')
|
||||
->addArgument('connection', Argument::OPTIONAL, 'The name of the queue connection to work', null)
|
||||
->addOption('queue', null, Option::VALUE_OPTIONAL, 'The queue to listen on')
|
||||
->addOption('once', null, Option::VALUE_NONE, 'Only process the next job on the queue')
|
||||
->addOption('delay', null, Option::VALUE_OPTIONAL, 'Amount of time to delay failed jobs', 0)
|
||||
->addOption('force', null, Option::VALUE_NONE, 'Force the worker to run even in maintenance mode')
|
||||
->addOption('memory', null, Option::VALUE_OPTIONAL, 'The memory limit in megabytes', 128)
|
||||
->addOption('timeout', null, Option::VALUE_OPTIONAL, 'The number of seconds a child process can run', 60)
|
||||
->addOption('sleep', null, Option::VALUE_OPTIONAL, 'Number of seconds to sleep when no job is available', 3)
|
||||
->addOption('tries', null, Option::VALUE_OPTIONAL, 'Number of times to attempt a job before logging it failed', 0)
|
||||
->setDescription('Process the next job on a queue');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
* @param Input $input
|
||||
* @param Output $output
|
||||
* @return int|null|void
|
||||
*/
|
||||
public function execute(Input $input, Output $output)
|
||||
{
|
||||
$connection = $input->getArgument('connection') ?: $this->app->config->get('queue.default');
|
||||
|
||||
$queue = $input->getOption('queue') ?: $this->app->config->get("queue.connections.{$connection}.queue", 'default');
|
||||
$delay = $input->getOption('delay');
|
||||
$sleep = $input->getOption('sleep');
|
||||
$tries = $input->getOption('tries');
|
||||
|
||||
$this->listenForEvents();
|
||||
|
||||
if ($input->getOption('once')) {
|
||||
$this->worker->runNextJob($connection, $queue, $delay, $sleep, $tries);
|
||||
} else {
|
||||
$memory = $input->getOption('memory');
|
||||
$timeout = $input->getOption('timeout');
|
||||
$this->worker->daemon($connection, $queue, $delay, $sleep, $tries, $memory, $timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件
|
||||
*/
|
||||
protected function listenForEvents()
|
||||
{
|
||||
$this->app->event->listen(JobProcessing::class, function (JobProcessing $event) {
|
||||
$this->writeOutput($event->job, 'starting');
|
||||
});
|
||||
|
||||
$this->app->event->listen(JobProcessed::class, function (JobProcessed $event) {
|
||||
$this->writeOutput($event->job, 'success');
|
||||
});
|
||||
|
||||
$this->app->event->listen(JobFailed::class, function (JobFailed $event) {
|
||||
$this->writeOutput($event->job, 'failed');
|
||||
|
||||
$this->logFailedJob($event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the status output for the queue worker.
|
||||
*
|
||||
* @param Job $job
|
||||
* @param $status
|
||||
*/
|
||||
protected function writeOutput(Job $job, $status)
|
||||
{
|
||||
switch ($status) {
|
||||
case 'starting':
|
||||
$this->writeStatus($job, 'Processing', 'comment');
|
||||
break;
|
||||
case 'success':
|
||||
$this->writeStatus($job, 'Processed', 'info');
|
||||
break;
|
||||
case 'failed':
|
||||
$this->writeStatus($job, 'Failed', 'error');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the status output for the queue worker.
|
||||
*
|
||||
* @param Job $job
|
||||
* @param string $status
|
||||
* @param string $type
|
||||
* @return void
|
||||
*/
|
||||
protected function writeStatus(Job $job, $status, $type)
|
||||
{
|
||||
$this->output->writeln(sprintf(
|
||||
"<{$type}>[%s][%s] %s</{$type}> %s",
|
||||
date('Y-m-d H:i:s'),
|
||||
$job->getJobId(),
|
||||
str_pad("{$status}:", 11),
|
||||
$job->getName()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录失败任务
|
||||
* @param JobFailed $event
|
||||
*/
|
||||
protected function logFailedJob(JobFailed $event)
|
||||
{
|
||||
$this->app['queue.failer']->log(
|
||||
$event->connection,
|
||||
$event->job->getQueue(),
|
||||
$event->job->getRawBody(),
|
||||
$event->exception
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
18
vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub
vendored
Executable file
18
vendor/topthink/think-queue/src/queue/command/stubs/failed_jobs.stub
vendored
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
use think\migration\db\Column;
|
||||
use think\migration\Migrator;
|
||||
|
||||
class CreateFailedJobsTable extends Migrator
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
$this->table('{{table}}')
|
||||
->addColumn(Column::text('connection'))
|
||||
->addColumn(Column::text('queue'))
|
||||
->addColumn(Column::longText('payload'))
|
||||
->addColumn(Column::longText('exception'))
|
||||
->addColumn(Column::timestamp('fail_time')->setDefault('CURRENT_TIMESTAMP'))
|
||||
->create();
|
||||
}
|
||||
}
|
||||
20
vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub
vendored
Executable file
20
vendor/topthink/think-queue/src/queue/command/stubs/jobs.stub
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
use think\migration\db\Column;
|
||||
use think\migration\Migrator;
|
||||
|
||||
class CreateJobsTable extends Migrator
|
||||
{
|
||||
public function change()
|
||||
{
|
||||
$this->table('{{table}}')
|
||||
->addColumn(Column::string('queue'))
|
||||
->addColumn(Column::longText('payload'))
|
||||
->addColumn(Column::tinyInteger('attempts')->setUnsigned())
|
||||
->addColumn(Column::unsignedInteger('reserve_time')->setNullable())
|
||||
->addColumn(Column::unsignedInteger('available_time'))
|
||||
->addColumn(Column::unsignedInteger('create_time'))
|
||||
->addIndex('queue')
|
||||
->create();
|
||||
}
|
||||
}
|
||||
228
vendor/topthink/think-queue/src/queue/connector/Database.php
vendored
Executable file
228
vendor/topthink/think-queue/src/queue/connector/Database.php
vendored
Executable file
@@ -0,0 +1,228 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\connector;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use stdClass;
|
||||
use think\Db;
|
||||
use think\db\ConnectionInterface;
|
||||
use think\db\Query;
|
||||
use think\queue\Connector;
|
||||
use think\queue\InteractsWithTime;
|
||||
use think\queue\job\Database as DatabaseJob;
|
||||
|
||||
class Database extends Connector
|
||||
{
|
||||
|
||||
use InteractsWithTime;
|
||||
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* The database table that holds the jobs.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
/**
|
||||
* The name of the default queue.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default;
|
||||
|
||||
/**
|
||||
* The expiration time of a job.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $retryAfter = 60;
|
||||
|
||||
public function __construct(ConnectionInterface $db, $table, $default = 'default', $retryAfter = 60)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->table = $table;
|
||||
$this->default = $default;
|
||||
$this->retryAfter = $retryAfter;
|
||||
}
|
||||
|
||||
public static function __make(Db $db, $config)
|
||||
{
|
||||
$connection = $db->connect($config['connection'] ?? null);
|
||||
|
||||
return new self($connection, $config['table'], $config['queue'], $config['retry_after'] ?? 60);
|
||||
}
|
||||
|
||||
public function size($queue = null)
|
||||
{
|
||||
return $this->db
|
||||
->name($this->table)
|
||||
->where('queue', $this->getQueue($queue))
|
||||
->count();
|
||||
}
|
||||
|
||||
public function push($job, $data = '', $queue = null)
|
||||
{
|
||||
return $this->pushToDatabase($queue, $this->createPayload($job, $data));
|
||||
}
|
||||
|
||||
public function pushRaw($payload, $queue = null, array $options = [])
|
||||
{
|
||||
return $this->pushToDatabase($queue, $payload);
|
||||
}
|
||||
|
||||
public function later($delay, $job, $data = '', $queue = null)
|
||||
{
|
||||
return $this->pushToDatabase($queue, $this->createPayload($job, $data), $delay);
|
||||
}
|
||||
|
||||
public function bulk($jobs, $data = '', $queue = null)
|
||||
{
|
||||
$queue = $this->getQueue($queue);
|
||||
|
||||
$availableAt = $this->availableAt();
|
||||
|
||||
return $this->db->name($this->table)->insertAll(collect((array) $jobs)->map(
|
||||
function ($job) use ($queue, $data, $availableAt) {
|
||||
return [
|
||||
'queue' => $queue,
|
||||
'attempts' => 0,
|
||||
'reserve_time' => null,
|
||||
'available_time' => $availableAt,
|
||||
'create_time' => $this->currentTime(),
|
||||
'payload' => $this->createPayload($job, $data),
|
||||
];
|
||||
}
|
||||
)->all());
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新发布任务
|
||||
*
|
||||
* @param string $queue
|
||||
* @param StdClass $job
|
||||
* @param int $delay
|
||||
* @return mixed
|
||||
*/
|
||||
public function release($queue, $job, $delay)
|
||||
{
|
||||
return $this->pushToDatabase($queue, $job->payload, $delay, $job->attempts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a raw payload to the database with a given delay.
|
||||
*
|
||||
* @param \DateTime|int $delay
|
||||
* @param string|null $queue
|
||||
* @param string $payload
|
||||
* @param int $attempts
|
||||
* @return mixed
|
||||
*/
|
||||
protected function pushToDatabase($queue, $payload, $delay = 0, $attempts = 0)
|
||||
{
|
||||
return $this->db->name($this->table)->insertGetId([
|
||||
'queue' => $this->getQueue($queue),
|
||||
'attempts' => $attempts,
|
||||
'reserve_time' => null,
|
||||
'available_time' => $this->availableAt($delay),
|
||||
'create_time' => $this->currentTime(),
|
||||
'payload' => $payload,
|
||||
]);
|
||||
}
|
||||
|
||||
public function pop($queue = null)
|
||||
{
|
||||
$queue = $this->getQueue($queue);
|
||||
|
||||
return $this->db->transaction(function () use ($queue) {
|
||||
|
||||
if ($job = $this->getNextAvailableJob($queue)) {
|
||||
|
||||
$job = $this->markJobAsReserved($job);
|
||||
|
||||
return new DatabaseJob($this->app, $this, $job, $this->connection, $queue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下个有效任务
|
||||
*
|
||||
* @param string|null $queue
|
||||
* @return StdClass|null
|
||||
*/
|
||||
protected function getNextAvailableJob($queue)
|
||||
{
|
||||
|
||||
$job = $this->db
|
||||
->name($this->table)
|
||||
->lock(true)
|
||||
->where('queue', $this->getQueue($queue))
|
||||
->where(function (Query $query) {
|
||||
$query->where(function (Query $query) {
|
||||
$query->whereNull('reserve_time')
|
||||
->where('available_time', '<=', $this->currentTime());
|
||||
});
|
||||
|
||||
//超时任务重试
|
||||
$expiration = Carbon::now()->subSeconds($this->retryAfter)->getTimestamp();
|
||||
|
||||
$query->whereOr(function (Query $query) use ($expiration) {
|
||||
$query->where('reserve_time', '<=', $expiration);
|
||||
});
|
||||
})
|
||||
->order('id', 'asc')
|
||||
->find();
|
||||
|
||||
return $job ? (object) $job : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标记任务正在执行.
|
||||
*
|
||||
* @param stdClass $job
|
||||
* @return stdClass
|
||||
*/
|
||||
protected function markJobAsReserved($job)
|
||||
{
|
||||
$this->db
|
||||
->name($this->table)
|
||||
->where('id', $job->id)
|
||||
->update([
|
||||
'reserve_time' => $job->reserve_time = $this->currentTime(),
|
||||
'attempts' => ++$job->attempts,
|
||||
]);
|
||||
|
||||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*
|
||||
* @param string $id
|
||||
* @return void
|
||||
*/
|
||||
public function deleteReserved($id)
|
||||
{
|
||||
$this->db->transaction(function () use ($id) {
|
||||
if ($this->db->name($this->table)->lock(true)->find($id)) {
|
||||
$this->db->name($this->table)->where('id', $id)->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected function getQueue($queue)
|
||||
{
|
||||
return $queue ?: $this->default;
|
||||
}
|
||||
}
|
||||
331
vendor/topthink/think-queue/src/queue/connector/Redis.php
vendored
Executable file
331
vendor/topthink/think-queue/src/queue/connector/Redis.php
vendored
Executable file
@@ -0,0 +1,331 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\connector;
|
||||
|
||||
use Closure;
|
||||
use Exception;
|
||||
use RedisException;
|
||||
use think\helper\Str;
|
||||
use think\queue\Connector;
|
||||
use think\queue\InteractsWithTime;
|
||||
use think\queue\job\Redis as RedisJob;
|
||||
|
||||
class Redis extends Connector
|
||||
{
|
||||
use InteractsWithTime;
|
||||
|
||||
/** @var \Redis */
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* The name of the default queue.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $default;
|
||||
|
||||
/**
|
||||
* The expiration time of a job.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $retryAfter = 60;
|
||||
|
||||
/**
|
||||
* The maximum number of seconds to block for a job.
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $blockFor = null;
|
||||
|
||||
public function __construct($redis, $default = 'default', $retryAfter = 60, $blockFor = null)
|
||||
{
|
||||
$this->redis = $redis;
|
||||
$this->default = $default;
|
||||
$this->retryAfter = $retryAfter;
|
||||
$this->blockFor = $blockFor;
|
||||
}
|
||||
|
||||
public static function __make($config)
|
||||
{
|
||||
if (!extension_loaded('redis')) {
|
||||
throw new Exception('redis扩展未安装');
|
||||
}
|
||||
|
||||
$redis = new class($config) {
|
||||
protected $config;
|
||||
protected $client;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->client = $this->createClient();
|
||||
}
|
||||
|
||||
protected function createClient()
|
||||
{
|
||||
$config = $this->config;
|
||||
$func = $config['persistent'] ? 'pconnect' : 'connect';
|
||||
|
||||
$client = new \Redis;
|
||||
$client->$func($config['host'], $config['port'], $config['timeout']);
|
||||
|
||||
if ('' != $config['password']) {
|
||||
$client->auth($config['password']);
|
||||
}
|
||||
|
||||
if (0 != $config['select']) {
|
||||
$client->select($config['select']);
|
||||
}
|
||||
return $client;
|
||||
}
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
try {
|
||||
return call_user_func_array([$this->client, $name], $arguments);
|
||||
} catch (RedisException $e) {
|
||||
if (Str::contains($e->getMessage(), 'went away')) {
|
||||
$this->client = $this->createClient();
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return new self($redis, $config['queue'], $config['retry_after'] ?? 60, $config['block_for'] ?? null);
|
||||
}
|
||||
|
||||
public function size($queue = null)
|
||||
{
|
||||
$queue = $this->getQueue($queue);
|
||||
|
||||
return $this->redis->lLen($queue) + $this->redis->zCard("{$queue}:delayed") + $this->redis->zCard("{$queue}:reserved");
|
||||
}
|
||||
|
||||
public function push($job, $data = '', $queue = null)
|
||||
{
|
||||
return $this->pushRaw($this->createPayload($job, $data), $queue);
|
||||
}
|
||||
|
||||
public function pushRaw($payload, $queue = null, array $options = [])
|
||||
{
|
||||
if ($this->redis->rPush($this->getQueue($queue), $payload)) {
|
||||
return json_decode($payload, true)['id'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
public function later($delay, $job, $data = '', $queue = null)
|
||||
{
|
||||
return $this->laterRaw($delay, $this->createPayload($job, $data), $queue);
|
||||
}
|
||||
|
||||
protected function laterRaw($delay, $payload, $queue = null)
|
||||
{
|
||||
if ($this->redis->zadd(
|
||||
$this->getQueue($queue) . ':delayed',
|
||||
$this->availableAt($delay),
|
||||
$payload
|
||||
)) {
|
||||
return json_decode($payload, true)['id'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
public function pop($queue = null)
|
||||
{
|
||||
$this->migrate($prefixed = $this->getQueue($queue));
|
||||
|
||||
if (empty($nextJob = $this->retrieveNextJob($prefixed))) {
|
||||
return;
|
||||
}
|
||||
|
||||
[$job, $reserved] = $nextJob;
|
||||
|
||||
if ($reserved) {
|
||||
return new RedisJob($this->app, $this, $job, $reserved, $this->connection, $queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate any delayed or expired jobs onto the primary queue.
|
||||
*
|
||||
* @param string $queue
|
||||
* @return void
|
||||
*/
|
||||
protected function migrate($queue)
|
||||
{
|
||||
$this->migrateExpiredJobs($queue . ':delayed', $queue);
|
||||
|
||||
if (!is_null($this->retryAfter)) {
|
||||
$this->migrateExpiredJobs($queue . ':reserved', $queue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动延迟任务
|
||||
*
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
* @param bool $attempt
|
||||
*/
|
||||
public function migrateExpiredJobs($from, $to, $attempt = true)
|
||||
{
|
||||
$this->redis->watch($from);
|
||||
|
||||
$jobs = $this->redis->zRangeByScore($from, '-inf', $this->currentTime());
|
||||
|
||||
if (!empty($jobs)) {
|
||||
$this->transaction(function () use ($from, $to, $jobs, $attempt) {
|
||||
|
||||
$this->redis->zRemRangeByRank($from, 0, count($jobs) - 1);
|
||||
|
||||
for ($i = 0; $i < count($jobs); $i += 100) {
|
||||
|
||||
$values = array_slice($jobs, $i, 100);
|
||||
|
||||
$this->redis->rPush($to, ...$values);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$this->redis->unwatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next job from the queue.
|
||||
*
|
||||
* @param string $queue
|
||||
* @return array
|
||||
*/
|
||||
protected function retrieveNextJob($queue)
|
||||
{
|
||||
if (!is_null($this->blockFor)) {
|
||||
return $this->blockingPop($queue);
|
||||
}
|
||||
|
||||
$job = $this->redis->lpop($queue);
|
||||
$reserved = false;
|
||||
|
||||
if ($job) {
|
||||
$reserved = json_decode($job, true);
|
||||
$reserved['attempts']++;
|
||||
$reserved = json_encode($reserved);
|
||||
$this->redis->zAdd($queue . ':reserved', $this->availableAt($this->retryAfter), $reserved);
|
||||
}
|
||||
|
||||
return [$job, $reserved];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next job by blocking-pop.
|
||||
*
|
||||
* @param string $queue
|
||||
* @return array
|
||||
*/
|
||||
protected function blockingPop($queue)
|
||||
{
|
||||
$rawBody = $this->redis->blpop($queue, $this->blockFor);
|
||||
|
||||
if (!empty($rawBody)) {
|
||||
$payload = json_decode($rawBody[1], true);
|
||||
|
||||
$payload['attempts']++;
|
||||
|
||||
$reserved = json_encode($payload);
|
||||
|
||||
$this->redis->zadd($queue . ':reserved', $this->availableAt($this->retryAfter), $reserved);
|
||||
|
||||
return [$rawBody[1], $reserved];
|
||||
}
|
||||
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*
|
||||
* @param string $queue
|
||||
* @param RedisJob $job
|
||||
* @return void
|
||||
*/
|
||||
public function deleteReserved($queue, $job)
|
||||
{
|
||||
$this->redis->zRem($this->getQueue($queue) . ':reserved', $job->getReservedJob());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a reserved job from the reserved queue and release it.
|
||||
*
|
||||
* @param string $queue
|
||||
* @param RedisJob $job
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAndRelease($queue, $job, $delay)
|
||||
{
|
||||
$queue = $this->getQueue($queue);
|
||||
|
||||
$reserved = $job->getReservedJob();
|
||||
|
||||
$this->redis->zRem($queue . ':reserved', $reserved);
|
||||
|
||||
$this->redis->zAdd($queue . ':delayed', $this->availableAt($delay), $reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
* redis事务
|
||||
* @param Closure $closure
|
||||
*/
|
||||
protected function transaction(Closure $closure)
|
||||
{
|
||||
$this->redis->multi();
|
||||
try {
|
||||
call_user_func($closure);
|
||||
if (!$this->redis->exec()) {
|
||||
$this->redis->discard();
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$this->redis->discard();
|
||||
}
|
||||
}
|
||||
|
||||
protected function createPayloadArray($job, $data = '')
|
||||
{
|
||||
return array_merge(parent::createPayloadArray($job, $data), [
|
||||
'id' => $this->getRandomId(),
|
||||
'attempts' => 0,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 随机id
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getRandomId()
|
||||
{
|
||||
return Str::random(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取队列名
|
||||
*
|
||||
* @param string|null $queue
|
||||
* @return string
|
||||
*/
|
||||
protected function getQueue($queue)
|
||||
{
|
||||
$queue = $queue ?: $this->default;
|
||||
return "{queues:{$queue}}";
|
||||
}
|
||||
}
|
||||
74
vendor/topthink/think-queue/src/queue/connector/Sync.php
vendored
Executable file
74
vendor/topthink/think-queue/src/queue/connector/Sync.php
vendored
Executable file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\connector;
|
||||
|
||||
use Exception;
|
||||
use think\queue\Connector;
|
||||
use think\queue\event\JobFailed;
|
||||
use think\queue\event\JobProcessed;
|
||||
use think\queue\event\JobProcessing;
|
||||
use think\queue\job\Sync as SyncJob;
|
||||
use Throwable;
|
||||
|
||||
class Sync extends Connector
|
||||
{
|
||||
|
||||
public function size($queue = null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function push($job, $data = '', $queue = null)
|
||||
{
|
||||
$queueJob = $this->resolveJob($this->createPayload($job, $data), $queue);
|
||||
|
||||
try {
|
||||
$this->triggerEvent(new JobProcessing($this->connection, $job));
|
||||
|
||||
$queueJob->fire();
|
||||
|
||||
$this->triggerEvent(new JobProcessed($this->connection, $job));
|
||||
} catch (Exception | Throwable $e) {
|
||||
|
||||
$this->triggerEvent(new JobFailed($this->connection, $job, $e));
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function triggerEvent($event)
|
||||
{
|
||||
$this->app->event->trigger($event);
|
||||
}
|
||||
|
||||
public function pop($queue = null)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected function resolveJob($payload, $queue)
|
||||
{
|
||||
return new SyncJob($this->app, $payload, $this->connection, $queue);
|
||||
}
|
||||
|
||||
public function pushRaw($payload, $queue = null, array $options = [])
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function later($delay, $job, $data = '', $queue = null)
|
||||
{
|
||||
return $this->push($job, $data, $queue);
|
||||
}
|
||||
}
|
||||
45
vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php
vendored
Executable file
45
vendor/topthink/think-queue/src/queue/event/JobExceptionOccurred.php
vendored
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\event;
|
||||
|
||||
use Exception;
|
||||
use think\queue\Job;
|
||||
|
||||
class JobExceptionOccurred
|
||||
{
|
||||
/**
|
||||
* The connection name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connectionName;
|
||||
|
||||
/**
|
||||
* The job instance.
|
||||
*
|
||||
* @var Job
|
||||
*/
|
||||
public $job;
|
||||
|
||||
/**
|
||||
* The exception instance.
|
||||
*
|
||||
* @var Exception
|
||||
*/
|
||||
public $exception;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param string $connectionName
|
||||
* @param Job $job
|
||||
* @param Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($connectionName, $job, $exception)
|
||||
{
|
||||
$this->job = $job;
|
||||
$this->exception = $exception;
|
||||
$this->connectionName = $connectionName;
|
||||
}
|
||||
}
|
||||
24
vendor/topthink/think-queue/src/queue/event/JobFailed.php
vendored
Executable file
24
vendor/topthink/think-queue/src/queue/event/JobFailed.php
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\event;
|
||||
|
||||
use think\queue\Job;
|
||||
|
||||
class JobFailed
|
||||
{
|
||||
/** @var string */
|
||||
public $connection;
|
||||
|
||||
/** @var Job */
|
||||
public $job;
|
||||
|
||||
/** @var \Exception */
|
||||
public $exception;
|
||||
|
||||
public function __construct($connection, $job, $exception)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->job = $job;
|
||||
$this->exception = $exception;
|
||||
}
|
||||
}
|
||||
20
vendor/topthink/think-queue/src/queue/event/JobProcessed.php
vendored
Executable file
20
vendor/topthink/think-queue/src/queue/event/JobProcessed.php
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\event;
|
||||
|
||||
use think\queue\Job;
|
||||
|
||||
class JobProcessed
|
||||
{
|
||||
/** @var string */
|
||||
public $connection;
|
||||
|
||||
/** @var Job */
|
||||
public $job;
|
||||
|
||||
public function __construct($connection, $job)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->job = $job;
|
||||
}
|
||||
}
|
||||
20
vendor/topthink/think-queue/src/queue/event/JobProcessing.php
vendored
Executable file
20
vendor/topthink/think-queue/src/queue/event/JobProcessing.php
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\event;
|
||||
|
||||
use think\queue\Job;
|
||||
|
||||
class JobProcessing
|
||||
{
|
||||
/** @var string */
|
||||
public $connection;
|
||||
|
||||
/** @var Job */
|
||||
public $job;
|
||||
|
||||
public function __construct($connection, $job)
|
||||
{
|
||||
$this->connection = $connection;
|
||||
$this->job = $job;
|
||||
}
|
||||
}
|
||||
24
vendor/topthink/think-queue/src/queue/event/WorkerStopping.php
vendored
Executable file
24
vendor/topthink/think-queue/src/queue/event/WorkerStopping.php
vendored
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\event;
|
||||
|
||||
class WorkerStopping
|
||||
{
|
||||
/**
|
||||
* The exit status.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $status;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
*
|
||||
* @param int $status
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($status = 0)
|
||||
{
|
||||
$this->status = $status;
|
||||
}
|
||||
}
|
||||
10
vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php
vendored
Executable file
10
vendor/topthink/think-queue/src/queue/exception/MaxAttemptsExceededException.php
vendored
Executable file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class MaxAttemptsExceededException extends RuntimeException
|
||||
{
|
||||
|
||||
}
|
||||
103
vendor/topthink/think-queue/src/queue/failed/Database.php
vendored
Executable file
103
vendor/topthink/think-queue/src/queue/failed/Database.php
vendored
Executable file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\failed;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use think\Db;
|
||||
use think\queue\FailedJob;
|
||||
|
||||
class Database extends FailedJob
|
||||
{
|
||||
|
||||
/** @var Db */
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* The database table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table;
|
||||
|
||||
public function __construct(Db $db, $table)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->table = $table;
|
||||
}
|
||||
|
||||
public static function __make(Db $db, $config)
|
||||
{
|
||||
return new self($db, $config['table']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a failed job into storage.
|
||||
*
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param string $payload
|
||||
* @param \Exception $exception
|
||||
* @return int|null
|
||||
*/
|
||||
public function log($connection, $queue, $payload, $exception)
|
||||
{
|
||||
$fail_time = Carbon::now()->toDateTimeString();
|
||||
|
||||
$exception = (string) $exception;
|
||||
|
||||
return $this->getTable()->insertGetId(compact(
|
||||
'connection',
|
||||
'queue',
|
||||
'payload',
|
||||
'exception',
|
||||
'fail_time'
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all of the failed jobs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return collect($this->getTable()->order('id', 'desc')->select())->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single failed job.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return object|null
|
||||
*/
|
||||
public function find($id)
|
||||
{
|
||||
return $this->getTable()->find($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single failed job from storage.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return bool
|
||||
*/
|
||||
public function forget($id)
|
||||
{
|
||||
return $this->getTable()->where('id', $id)->delete() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all of the failed jobs from storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
$this->getTable()->delete(true);
|
||||
}
|
||||
|
||||
protected function getTable()
|
||||
{
|
||||
return $this->db->name($this->table);
|
||||
}
|
||||
}
|
||||
63
vendor/topthink/think-queue/src/queue/failed/None.php
vendored
Executable file
63
vendor/topthink/think-queue/src/queue/failed/None.php
vendored
Executable file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace think\queue\failed;
|
||||
|
||||
use think\queue\FailedJob;
|
||||
|
||||
class None extends FailedJob
|
||||
{
|
||||
|
||||
/**
|
||||
* Log a failed job into storage.
|
||||
*
|
||||
* @param string $connection
|
||||
* @param string $queue
|
||||
* @param string $payload
|
||||
* @param \Exception $exception
|
||||
*/
|
||||
public function log($connection, $queue, $payload, $exception)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all of the failed jobs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a single failed job.
|
||||
*
|
||||
* @param mixed $id
|
||||
*/
|
||||
public function find($id)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single failed job from storage.
|
||||
*
|
||||
* @param mixed $id
|
||||
* @return bool
|
||||
*/
|
||||
public function forget($id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all of the failed jobs from storage.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function flush()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
91
vendor/topthink/think-queue/src/queue/job/Database.php
vendored
Executable file
91
vendor/topthink/think-queue/src/queue/job/Database.php
vendored
Executable file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
namespace think\queue\job;
|
||||
|
||||
use think\App;
|
||||
use think\queue\connector\Database as DatabaseQueue;
|
||||
use think\queue\Job;
|
||||
|
||||
class Database extends Job
|
||||
{
|
||||
/**
|
||||
* The database queue instance.
|
||||
* @var DatabaseQueue
|
||||
*/
|
||||
protected $database;
|
||||
|
||||
/**
|
||||
* The database job payload.
|
||||
* @var Object
|
||||
*/
|
||||
protected $job;
|
||||
|
||||
public function __construct(App $app, DatabaseQueue $database, $job, $connection, $queue)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->job = $job;
|
||||
$this->queue = $queue;
|
||||
$this->database = $database;
|
||||
$this->connection = $connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
parent::delete();
|
||||
$this->database->deleteReserved($this->job->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新发布任务
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
public function release($delay = 0)
|
||||
{
|
||||
parent::release($delay);
|
||||
|
||||
$this->delete();
|
||||
|
||||
$this->database->release($this->queue, $this->job, $delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前任务尝试次数
|
||||
* @return int
|
||||
*/
|
||||
public function attempts()
|
||||
{
|
||||
return (int) $this->job->attempts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw body string for the job.
|
||||
* @return string
|
||||
*/
|
||||
public function getRawBody()
|
||||
{
|
||||
return $this->job->payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getJobId()
|
||||
{
|
||||
return $this->job->id;
|
||||
}
|
||||
}
|
||||
112
vendor/topthink/think-queue/src/queue/job/Redis.php
vendored
Executable file
112
vendor/topthink/think-queue/src/queue/job/Redis.php
vendored
Executable file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\job;
|
||||
|
||||
use think\App;
|
||||
use think\queue\connector\Redis as RedisQueue;
|
||||
use think\queue\Job;
|
||||
|
||||
class Redis extends Job
|
||||
{
|
||||
|
||||
/**
|
||||
* The redis queue instance.
|
||||
* @var RedisQueue
|
||||
*/
|
||||
protected $redis;
|
||||
|
||||
/**
|
||||
* The database job payload.
|
||||
* @var Object
|
||||
*/
|
||||
protected $job;
|
||||
|
||||
/**
|
||||
* The Redis job payload inside the reserved queue.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $reserved;
|
||||
|
||||
public function __construct(App $app, RedisQueue $redis, $job, $reserved, $connection, $queue)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->job = $job;
|
||||
$this->queue = $queue;
|
||||
$this->connection = $connection;
|
||||
$this->redis = $redis;
|
||||
$this->reserved = $reserved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of times the job has been attempted.
|
||||
* @return int
|
||||
*/
|
||||
public function attempts()
|
||||
{
|
||||
return $this->payload('attempts') + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw body string for the job.
|
||||
* @return string
|
||||
*/
|
||||
public function getRawBody()
|
||||
{
|
||||
return $this->job;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除任务
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
parent::delete();
|
||||
|
||||
$this->redis->deleteReserved($this->queue, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新发布任务
|
||||
*
|
||||
* @param int $delay
|
||||
* @return void
|
||||
*/
|
||||
public function release($delay = 0)
|
||||
{
|
||||
parent::release($delay);
|
||||
|
||||
$this->redis->deleteAndRelease($this->queue, $this, $delay);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getJobId()
|
||||
{
|
||||
return $this->payload('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying reserved Redis job.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getReservedJob()
|
||||
{
|
||||
return $this->reserved;
|
||||
}
|
||||
}
|
||||
66
vendor/topthink/think-queue/src/queue/job/Sync.php
vendored
Executable file
66
vendor/topthink/think-queue/src/queue/job/Sync.php
vendored
Executable file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
// +----------------------------------------------------------------------
|
||||
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
|
||||
// +----------------------------------------------------------------------
|
||||
// | Copyright (c) 2006-2015 http://thinkphp.cn All rights reserved.
|
||||
// +----------------------------------------------------------------------
|
||||
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
|
||||
// +----------------------------------------------------------------------
|
||||
// | Author: yunwuxin <448901948@qq.com>
|
||||
// +----------------------------------------------------------------------
|
||||
|
||||
namespace think\queue\job;
|
||||
|
||||
use think\App;
|
||||
use think\queue\Job;
|
||||
|
||||
class Sync extends Job
|
||||
{
|
||||
/**
|
||||
* The queue message data.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $job;
|
||||
|
||||
public function __construct(App $app, $job, $connection, $queue)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->connection = $connection;
|
||||
$this->queue = $queue;
|
||||
$this->job = $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of times the job has been attempted.
|
||||
* @return int
|
||||
*/
|
||||
public function attempts()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the raw body string for the job.
|
||||
* @return string
|
||||
*/
|
||||
public function getRawBody()
|
||||
{
|
||||
return $this->job;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the job identifier.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getJobId()
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function getQueue()
|
||||
{
|
||||
return 'sync';
|
||||
}
|
||||
}
|
||||
123
vendor/topthink/think-queue/tests/DatabaseConnectorTest.php
vendored
Executable file
123
vendor/topthink/think-queue/tests/DatabaseConnectorTest.php
vendored
Executable file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace think\test\queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Mockery as m;
|
||||
use Mockery\MockInterface;
|
||||
use ReflectionClass;
|
||||
use stdClass;
|
||||
use think\Db;
|
||||
use think\queue\Connector;
|
||||
use think\queue\connector\Database;
|
||||
|
||||
class DatabaseConnectorTest extends TestCase
|
||||
{
|
||||
/** @var Database|MockInterface */
|
||||
protected $connector;
|
||||
|
||||
/** @var Db|MockInterface */
|
||||
protected $db;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->db = m::mock(Db::class);
|
||||
$this->connector = new Database($this->db, 'table', 'default');
|
||||
}
|
||||
|
||||
public function testPushProperlyPushesJobOntoDatabase()
|
||||
{
|
||||
$this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
|
||||
|
||||
$query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) {
|
||||
$this->assertEquals('default', $array['queue']);
|
||||
$this->assertEquals(json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
|
||||
$this->assertEquals(0, $array['attempts']);
|
||||
$this->assertNull($array['reserved_at']);
|
||||
$this->assertInternalType('int', $array['available_at']);
|
||||
});
|
||||
$this->connector->push('foo', ['data']);
|
||||
}
|
||||
|
||||
public function testDelayedPushProperlyPushesJobOntoDatabase()
|
||||
{
|
||||
$this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
|
||||
|
||||
$query->shouldReceive('insertGetId')->once()->andReturnUsing(function ($array) {
|
||||
$this->assertEquals('default', $array['queue']);
|
||||
$this->assertEquals(json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]), $array['payload']);
|
||||
$this->assertEquals(0, $array['attempts']);
|
||||
$this->assertNull($array['reserved_at']);
|
||||
$this->assertInternalType('int', $array['available_at']);
|
||||
});
|
||||
|
||||
$this->connector->later(10, 'foo', ['data']);
|
||||
}
|
||||
|
||||
public function testFailureToCreatePayloadFromObject()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
|
||||
$job = new stdClass;
|
||||
$job->invalid = "\xc3\x28";
|
||||
|
||||
$queue = $this->getMockForAbstractClass(Connector::class);
|
||||
$class = new ReflectionClass(Connector::class);
|
||||
|
||||
$createPayload = $class->getMethod('createPayload');
|
||||
$createPayload->setAccessible(true);
|
||||
$createPayload->invokeArgs($queue, [
|
||||
$job,
|
||||
'queue-name',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testFailureToCreatePayloadFromArray()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
|
||||
$queue = $this->getMockForAbstractClass(Connector::class);
|
||||
$class = new ReflectionClass(Connector::class);
|
||||
|
||||
$createPayload = $class->getMethod('createPayload');
|
||||
$createPayload->setAccessible(true);
|
||||
$createPayload->invokeArgs($queue, [
|
||||
["\xc3\x28"],
|
||||
'queue-name',
|
||||
]);
|
||||
}
|
||||
|
||||
public function testBulkBatchPushesOntoDatabase()
|
||||
{
|
||||
|
||||
$this->db->shouldReceive('name')->with('table')->andReturn($query = m::mock(stdClass::class));
|
||||
|
||||
Carbon::setTestNow(
|
||||
$now = Carbon::now()->addSeconds()
|
||||
);
|
||||
|
||||
$query->shouldReceive('insertAll')->once()->andReturnUsing(function ($records) use ($now) {
|
||||
$this->assertEquals([
|
||||
[
|
||||
'queue' => 'queue',
|
||||
'payload' => json_encode(['job' => 'foo', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]),
|
||||
'attempts' => 0,
|
||||
'reserved_at' => null,
|
||||
'available_at' => $now->getTimestamp(),
|
||||
'created_at' => $now->getTimestamp(),
|
||||
], [
|
||||
'queue' => 'queue',
|
||||
'payload' => json_encode(['job' => 'bar', 'maxTries' => null, 'timeout' => null, 'data' => ['data']]),
|
||||
'attempts' => 0,
|
||||
'reserved_at' => null,
|
||||
'available_at' => $now->getTimestamp(),
|
||||
'created_at' => $now->getTimestamp(),
|
||||
],
|
||||
], $records);
|
||||
});
|
||||
|
||||
$this->connector->bulk(['foo', 'bar'], ['data'], 'queue');
|
||||
}
|
||||
|
||||
}
|
||||
80
vendor/topthink/think-queue/tests/ListenerTest.php
vendored
Executable file
80
vendor/topthink/think-queue/tests/ListenerTest.php
vendored
Executable file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
namespace think\test\queue;
|
||||
|
||||
use Mockery as m;
|
||||
use Mockery\MockInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
use think\queue\Listener;
|
||||
|
||||
class ListenerTest extends TestCase
|
||||
{
|
||||
/** @var Process|MockInterface */
|
||||
protected $process;
|
||||
/** @var Listener|MockInterface */
|
||||
protected $listener;
|
||||
|
||||
public function testRunProcessCallsProcess()
|
||||
{
|
||||
/** @var Process|MockInterface $process */
|
||||
$process = m::mock(Process::class)->makePartial();
|
||||
$process->shouldReceive('run')->once();
|
||||
/** @var Listener|MockInterface $listener */
|
||||
$listener = m::mock(Listener::class)->makePartial();
|
||||
$listener->shouldReceive('memoryExceeded')->once()->with(1)->andReturn(false);
|
||||
|
||||
$listener->runProcess($process, 1);
|
||||
}
|
||||
|
||||
public function testListenerStopsWhenMemoryIsExceeded()
|
||||
{
|
||||
/** @var Process|MockInterface $process */
|
||||
$process = m::mock(Process::class)->makePartial();
|
||||
$process->shouldReceive('run')->once();
|
||||
/** @var Listener|MockInterface $listener */
|
||||
$listener = m::mock(Listener::class)->makePartial();
|
||||
$listener->shouldReceive('memoryExceeded')->once()->with(1)->andReturn(true);
|
||||
$listener->shouldReceive('stop')->once();
|
||||
|
||||
$listener->runProcess($process, 1);
|
||||
}
|
||||
|
||||
public function testMakeProcessCorrectlyFormatsCommandLine()
|
||||
{
|
||||
$listener = new Listener(__DIR__);
|
||||
|
||||
$process = $listener->makeProcess('connection', 'queue', 1, 3, 0, 2, 3);
|
||||
$escape = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
|
||||
|
||||
$this->assertInstanceOf(Process::class, $process);
|
||||
$this->assertEquals(__DIR__, $process->getWorkingDirectory());
|
||||
$this->assertEquals(3, $process->getTimeout());
|
||||
$this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}connection{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
|
||||
}
|
||||
|
||||
public function testMakeProcessCorrectlyFormatsCommandLineWithAnEnvironmentSpecified()
|
||||
{
|
||||
$listener = new Listener(__DIR__);
|
||||
|
||||
$process = $listener->makeProcess('connection', 'queue', 1, 3, 0, 2, 3);
|
||||
$escape = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
|
||||
|
||||
$this->assertInstanceOf(Process::class, $process);
|
||||
$this->assertEquals(__DIR__, $process->getWorkingDirectory());
|
||||
$this->assertEquals(3, $process->getTimeout());
|
||||
$this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}connection{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
|
||||
}
|
||||
|
||||
public function testMakeProcessCorrectlyFormatsCommandLineWhenTheConnectionIsNotSpecified()
|
||||
{
|
||||
$listener = new Listener(__DIR__);
|
||||
|
||||
$process = $listener->makeProcess(null, 'queue', 1, 3, 0, 2, 3);
|
||||
$escape = '\\' === DIRECTORY_SEPARATOR ? '"' : '\'';
|
||||
|
||||
$this->assertInstanceOf(Process::class, $process);
|
||||
$this->assertEquals(__DIR__, $process->getWorkingDirectory());
|
||||
$this->assertEquals(3, $process->getTimeout());
|
||||
$this->assertEquals($escape . PHP_BINARY . $escape . " {$escape}think{$escape} {$escape}queue:work{$escape} {$escape}--once{$escape} {$escape}--queue=queue{$escape} {$escape}--delay=1{$escape} {$escape}--memory=2{$escape} {$escape}--sleep=3{$escape} {$escape}--tries=0{$escape}", $process->getCommandLine());
|
||||
}
|
||||
}
|
||||
49
vendor/topthink/think-queue/tests/QueueTest.php
vendored
Executable file
49
vendor/topthink/think-queue/tests/QueueTest.php
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace think\test\queue;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use Mockery as m;
|
||||
use think\Config;
|
||||
use think\Queue;
|
||||
use think\queue\connector\Sync;
|
||||
|
||||
class QueueTest extends TestCase
|
||||
{
|
||||
/** @var Queue */
|
||||
protected $queue;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->queue = new Queue($this->app);
|
||||
}
|
||||
|
||||
public function testDefaultConnectionCanBeResolved()
|
||||
{
|
||||
$sync = new Sync();
|
||||
|
||||
$this->app->shouldReceive('invokeClass')->once()->with('\think\queue\connector\Sync', [['driver' => 'sync']])->andReturn($sync);
|
||||
|
||||
$config = m::mock(Config::class);
|
||||
|
||||
$config->shouldReceive('get')->twice()->with('queue.connectors.sync', ['driver' => 'sync'])->andReturn(['driver' => 'sync']);
|
||||
$config->shouldReceive('get')->once()->with('queue.default', 'sync')->andReturn('sync');
|
||||
|
||||
$this->app->shouldReceive('get')->times(3)->with('config')->andReturn($config);
|
||||
|
||||
$this->assertSame($sync, $this->queue->driver('sync'));
|
||||
$this->assertSame($sync, $this->queue->driver());
|
||||
}
|
||||
|
||||
public function testNotSupportDriver()
|
||||
{
|
||||
$config = m::mock(Config::class);
|
||||
|
||||
$config->shouldReceive('get')->once()->with('queue.connectors.hello', ['driver' => 'sync'])->andReturn(['driver' => 'hello']);
|
||||
$this->app->shouldReceive('get')->once()->with('config')->andReturn($config);
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->queue->driver('hello');
|
||||
}
|
||||
}
|
||||
23
vendor/topthink/think-queue/tests/TestCase.php
vendored
Executable file
23
vendor/topthink/think-queue/tests/TestCase.php
vendored
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace think\test\queue;
|
||||
|
||||
use Mockery as m;
|
||||
use Mockery\MockInterface;
|
||||
use think\App;
|
||||
|
||||
abstract class TestCase extends \PHPUnit\Framework\TestCase
|
||||
{
|
||||
/** @var App|MockInterface */
|
||||
protected $app;
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
m::close();
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->app = m::mock(App::class)->makePartial();
|
||||
}
|
||||
}
|
||||
420
vendor/topthink/think-queue/tests/WorkerTest.php
vendored
Executable file
420
vendor/topthink/think-queue/tests/WorkerTest.php
vendored
Executable file
@@ -0,0 +1,420 @@
|
||||
<?php
|
||||
|
||||
namespace think\test\queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Mockery as m;
|
||||
use Mockery\MockInterface;
|
||||
use RuntimeException;
|
||||
use think\Cache;
|
||||
use think\Event;
|
||||
use think\exception\Handle;
|
||||
use think\Queue;
|
||||
use think\queue\connector\Sync;
|
||||
use think\queue\event\JobExceptionOccurred;
|
||||
use think\queue\event\JobFailed;
|
||||
use think\queue\event\JobProcessed;
|
||||
use think\queue\event\JobProcessing;
|
||||
use think\queue\exception\MaxAttemptsExceededException;
|
||||
|
||||
class WorkerTest extends TestCase
|
||||
{
|
||||
/** @var Handle|MockInterface */
|
||||
protected $handle;
|
||||
|
||||
/** @var Event|MockInterface */
|
||||
protected $event;
|
||||
|
||||
/** @var Cache|MockInterface */
|
||||
protected $cache;
|
||||
|
||||
/** @var Queue|MockInterface */
|
||||
protected $queue;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->queue = m::mock(Queue::class);
|
||||
$this->handle = m::spy(Handle::class);
|
||||
$this->event = m::spy(Event::class);
|
||||
$this->cache = m::spy(Cache::class);
|
||||
}
|
||||
|
||||
public function testJobCanBeFired()
|
||||
{
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job = new WorkerFakeJob]]);
|
||||
|
||||
$this->event->shouldReceive('trigger')->with(m::type(JobProcessing::class))->once();
|
||||
$this->event->shouldReceive('trigger')->with(m::type(JobProcessed::class))->once();
|
||||
|
||||
$worker->runNextJob('sync', 'default');
|
||||
}
|
||||
|
||||
public function testWorkerCanWorkUntilQueueIsEmpty()
|
||||
{
|
||||
$worker = $this->getWorker(['default' => [
|
||||
$firstJob = new WorkerFakeJob,
|
||||
$secondJob = new WorkerFakeJob,
|
||||
]]);
|
||||
|
||||
$this->expectException(LoopBreakerException::class);
|
||||
|
||||
$worker->daemon('sync', 'default');
|
||||
|
||||
$this->assertTrue($firstJob->fired);
|
||||
|
||||
$this->assertTrue($secondJob->fired);
|
||||
|
||||
$this->assertSame(0, $worker->stoppedWithStatus);
|
||||
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobProcessing::class))->twice();
|
||||
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobProcessed::class))->twice();
|
||||
}
|
||||
|
||||
public function testJobCanBeFiredBasedOnPriority()
|
||||
{
|
||||
$worker = $this->getWorker([
|
||||
'high' => [
|
||||
$highJob = new WorkerFakeJob,
|
||||
$secondHighJob = new WorkerFakeJob,
|
||||
],
|
||||
'low' => [$lowJob = new WorkerFakeJob],
|
||||
]);
|
||||
|
||||
$worker->runNextJob('sync', 'high,low');
|
||||
|
||||
$this->assertTrue($highJob->fired);
|
||||
$this->assertFalse($secondHighJob->fired);
|
||||
$this->assertFalse($lowJob->fired);
|
||||
|
||||
$worker->runNextJob('sync', 'high,low');
|
||||
$this->assertTrue($secondHighJob->fired);
|
||||
$this->assertFalse($lowJob->fired);
|
||||
|
||||
$worker->runNextJob('sync', 'high,low');
|
||||
$this->assertTrue($lowJob->fired);
|
||||
}
|
||||
|
||||
public function testExceptionIsReportedIfConnectionThrowsExceptionOnJobPop()
|
||||
{
|
||||
$e = new RuntimeException();
|
||||
|
||||
$sync = m::mock(Sync::class);
|
||||
|
||||
$sync->shouldReceive('pop')->andReturnUsing(function () use ($e) {
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$this->queue->shouldReceive('driver')->with('sync')->andReturn($sync);
|
||||
|
||||
$worker = new Worker($this->queue, $this->event, $this->handle);
|
||||
|
||||
$worker->runNextJob('sync', 'default');
|
||||
|
||||
$this->handle->shouldHaveReceived('report')->with($e);
|
||||
}
|
||||
|
||||
public function testWorkerSleepsWhenQueueIsEmpty()
|
||||
{
|
||||
$worker = $this->getWorker(['default' => []]);
|
||||
$worker->runNextJob('sync', 'default', 0, 5);
|
||||
$this->assertEquals(5, $worker->sleptFor);
|
||||
}
|
||||
|
||||
public function testJobIsReleasedOnException()
|
||||
{
|
||||
$e = new RuntimeException;
|
||||
|
||||
$job = new WorkerFakeJob(function () use ($e) {
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default', 10);
|
||||
|
||||
$this->assertEquals(10, $job->releaseAfter);
|
||||
$this->assertFalse($job->deleted);
|
||||
$this->handle->shouldHaveReceived('report')->with($e);
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
|
||||
$this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
|
||||
}
|
||||
|
||||
public function testJobIsNotReleasedIfItHasExceededMaxAttempts()
|
||||
{
|
||||
$e = new RuntimeException;
|
||||
|
||||
$job = new WorkerFakeJob(function ($job) use ($e) {
|
||||
// In normal use this would be incremented by being popped off the queue
|
||||
$job->attempts++;
|
||||
|
||||
throw $e;
|
||||
});
|
||||
$job->attempts = 1;
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default', 0, 3, 1);
|
||||
|
||||
$this->assertNull($job->releaseAfter);
|
||||
$this->assertTrue($job->deleted);
|
||||
$this->assertEquals($e, $job->failedWith);
|
||||
$this->handle->shouldHaveReceived('report')->with($e);
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
|
||||
$this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
|
||||
}
|
||||
|
||||
public function testJobIsNotReleasedIfItHasExpired()
|
||||
{
|
||||
$e = new RuntimeException;
|
||||
|
||||
$job = new WorkerFakeJob(function ($job) use ($e) {
|
||||
// In normal use this would be incremented by being popped off the queue
|
||||
$job->attempts++;
|
||||
|
||||
throw $e;
|
||||
});
|
||||
|
||||
$job->timeoutAt = Carbon::now()->addSeconds(1)->getTimestamp();
|
||||
|
||||
$job->attempts = 0;
|
||||
|
||||
Carbon::setTestNow(
|
||||
Carbon::now()->addSeconds(1)
|
||||
);
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default');
|
||||
|
||||
$this->assertNull($job->releaseAfter);
|
||||
$this->assertTrue($job->deleted);
|
||||
$this->assertEquals($e, $job->failedWith);
|
||||
$this->handle->shouldHaveReceived('report')->with($e);
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
|
||||
$this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
|
||||
}
|
||||
|
||||
public function testJobIsFailedIfItHasAlreadyExceededMaxAttempts()
|
||||
{
|
||||
$job = new WorkerFakeJob(function ($job) {
|
||||
$job->attempts++;
|
||||
});
|
||||
|
||||
$job->attempts = 2;
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default', 0, 3, 1);
|
||||
|
||||
$this->assertNull($job->releaseAfter);
|
||||
$this->assertTrue($job->deleted);
|
||||
$this->assertInstanceOf(MaxAttemptsExceededException::class, $job->failedWith);
|
||||
$this->handle->shouldHaveReceived('report')->with(m::type(MaxAttemptsExceededException::class));
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
|
||||
$this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
|
||||
}
|
||||
|
||||
public function testJobIsFailedIfItHasAlreadyExpired()
|
||||
{
|
||||
$job = new WorkerFakeJob(function ($job) {
|
||||
$job->attempts++;
|
||||
});
|
||||
|
||||
$job->timeoutAt = Carbon::now()->addSeconds(2)->getTimestamp();
|
||||
|
||||
$job->attempts = 1;
|
||||
|
||||
Carbon::setTestNow(
|
||||
Carbon::now()->addSeconds(3)
|
||||
);
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default');
|
||||
|
||||
$this->assertNull($job->releaseAfter);
|
||||
$this->assertTrue($job->deleted);
|
||||
$this->assertInstanceOf(MaxAttemptsExceededException::class, $job->failedWith);
|
||||
$this->handle->shouldHaveReceived('report')->with(m::type(MaxAttemptsExceededException::class));
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobExceptionOccurred::class))->once();
|
||||
$this->event->shouldHaveReceived('trigger')->with(m::type(JobFailed::class))->once();
|
||||
$this->event->shouldNotHaveReceived('trigger', [m::type(JobProcessed::class)]);
|
||||
}
|
||||
|
||||
public function testJobBasedMaxRetries()
|
||||
{
|
||||
$job = new WorkerFakeJob(function ($job) {
|
||||
$job->attempts++;
|
||||
});
|
||||
|
||||
$job->attempts = 2;
|
||||
|
||||
$job->maxTries = 10;
|
||||
|
||||
$worker = $this->getWorker(['default' => [$job]]);
|
||||
$worker->runNextJob('sync', 'default', 0, 3, 1);
|
||||
|
||||
$this->assertFalse($job->deleted);
|
||||
$this->assertNull($job->failedWith);
|
||||
}
|
||||
|
||||
protected function getWorker($jobs)
|
||||
{
|
||||
$sync = m::mock(Sync::class);
|
||||
|
||||
$sync->shouldReceive('pop')->andReturnUsing(function ($queue) use (&$jobs) {
|
||||
return array_shift($jobs[$queue]);
|
||||
});
|
||||
|
||||
$this->queue->shouldReceive('driver')->with('sync')->andReturn($sync);
|
||||
|
||||
return new Worker($this->queue, $this->event, $this->handle, $this->cache);
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerFakeConnector
|
||||
{
|
||||
public $jobs = [];
|
||||
|
||||
public function __construct($jobs)
|
||||
{
|
||||
$this->jobs = $jobs;
|
||||
}
|
||||
|
||||
public function pop($queue)
|
||||
{
|
||||
return array_shift($this->jobs[$queue]);
|
||||
}
|
||||
}
|
||||
|
||||
class Worker extends \think\queue\Worker
|
||||
{
|
||||
public $sleptFor;
|
||||
|
||||
public $stoppedWithStatus;
|
||||
|
||||
public function sleep($seconds)
|
||||
{
|
||||
$this->sleptFor = $seconds;
|
||||
}
|
||||
|
||||
public function stop($status = 0)
|
||||
{
|
||||
$this->stoppedWithStatus = $status;
|
||||
|
||||
throw new LoopBreakerException;
|
||||
}
|
||||
|
||||
protected function stopIfNecessary($job, $lastRestart, $memory)
|
||||
{
|
||||
if (is_null($job)) {
|
||||
$this->stop();
|
||||
} else {
|
||||
parent::stopIfNecessary($job, $lastRestart, $memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class WorkerFakeJob
|
||||
{
|
||||
|
||||
public $fired = false;
|
||||
public $callback;
|
||||
public $deleted = false;
|
||||
public $releaseAfter;
|
||||
public $released = false;
|
||||
public $maxTries;
|
||||
public $timeoutAt;
|
||||
public $attempts = 0;
|
||||
public $failedWith;
|
||||
public $failed = false;
|
||||
public $connectionName;
|
||||
|
||||
public function __construct($callback = null)
|
||||
{
|
||||
$this->callback = $callback ?: function () {
|
||||
//
|
||||
};
|
||||
}
|
||||
|
||||
public function fire()
|
||||
{
|
||||
$this->fired = true;
|
||||
$this->callback->__invoke($this);
|
||||
}
|
||||
|
||||
public function payload()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function maxTries()
|
||||
{
|
||||
return $this->maxTries;
|
||||
}
|
||||
|
||||
public function timeoutAt()
|
||||
{
|
||||
return $this->timeoutAt;
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->deleted = true;
|
||||
}
|
||||
|
||||
public function isDeleted()
|
||||
{
|
||||
return $this->deleted;
|
||||
}
|
||||
|
||||
public function release($delay)
|
||||
{
|
||||
$this->released = true;
|
||||
|
||||
$this->releaseAfter = $delay;
|
||||
}
|
||||
|
||||
public function isReleased()
|
||||
{
|
||||
return $this->released;
|
||||
}
|
||||
|
||||
public function attempts()
|
||||
{
|
||||
return $this->attempts;
|
||||
}
|
||||
|
||||
public function markAsFailed()
|
||||
{
|
||||
$this->failed = true;
|
||||
}
|
||||
|
||||
public function failed($e)
|
||||
{
|
||||
$this->markAsFailed();
|
||||
|
||||
$this->failedWith = $e;
|
||||
}
|
||||
|
||||
public function hasFailed()
|
||||
{
|
||||
return $this->failed;
|
||||
}
|
||||
|
||||
public function timeout()
|
||||
{
|
||||
return time() + 60;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'WorkerFakeJob';
|
||||
}
|
||||
}
|
||||
|
||||
class LoopBreakerException extends RuntimeException
|
||||
{
|
||||
//
|
||||
}
|
||||
3
vendor/topthink/think-queue/tests/bootstrap.php
vendored
Executable file
3
vendor/topthink/think-queue/tests/bootstrap.php
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
include __DIR__.'/../vendor/autoload.php';
|
||||
Reference in New Issue
Block a user