如果您以前在服务器上使用过 Laravel 队列
,您可能已在 文档 中看到关于在队列进程因任何原因停止的情况下,需要在您的服务器上运行 Supervisor
以保持队列进程持续的章节。您通常需要做的就是在 配置 Supervisor,以便运行 queue:work
Artisan 命令。
现在我不会详细说明关于队列的不同Artisan命令如何在后台运行的细节,但如果我们仔细看看比如 queue:work
命令,基本上正在发生的情况是作业在一个 while loop
中运行,如果满足某些条件,工作进程将被停止,并退出命令。然后Supervisor将会再次运行它。
以下是一个简化后的循环示例(来自 Illuminate/Queue/Worker.php
文件中的 daemon
函数)
while (true) {
// Get the next job from the queue.
$job = $this->getNextJob(
$this->manager->connection($connectionName), $queue
);
// Run the job if needed.
if ($job) {
$jobsProcessed++;
$this->runJob($job, $connectionName, $options);
}
// Get the worker status.
$status = $this->stopIfNecessary(
$options, $lastRestart, $startTime, $jobsProcessed, $job
);
// Stop the worker if needed.
if (! is_null($status)) {
return $this->stop($status, $options);
}
}
<h2 class="subheading">自定义命令</h2>现在,如果我们以这个示例作为起点,我们可以创建一个自己的命令,该命令在需要的情况下可以通过Supervisor无限期地运行。
假设我们为家居工具商店创建了一个应用程序。用户选择一个商品,填写必要的信息,并为特定工具提交一个订单。一旦订单创建,它就会自动设置为待处理
状态。为了能够监控系统中的此类订单并加入我们自己的逻辑,我们可以创建一个自定义Artisan命令,它会定时检查所有待处理的订单,如果找到任何订单,我们可以调度一个Laravel任务来进一步处理该订单(也许检查工具是否库存充足,是否可以运送到用户的地址,并发送电子邮件告知用户订单状态)。
以下是一个此类命令的示例:
namespace App\Console\Commands;
use Illuminate\Console\Command;
class MonitorOrders extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'orders:monitor';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Monitor pending tool orders and dispatch them for processing.';
/**
* Execute the console command.
*/
public function handle()
{
while (true) {
// Get all the orders with the pending status
$orders = Order::where('status', 'pending')->get();
// If there are no orders with a pending status wait for a second then check again
if ($orders->doesntExist()) {
sleep(1);
continue;
}
foreach ($orders as $order) {
// Dispatch the order for further processing to a job
ProcessOrder::dispatch($order)
->onQueue('orders')
->onConnection('database');
}
sleep(1); // Potentially take a break between runs
}
}
}
<h2 class="subheading">最大作业选项</h2>如果我们想指定应处理的作业数量,类似于<a title="指定作业数量" href="https://laravel.net.cn/docs/10.x/queues#processing-a-specified-number-of-jobs" target="_blank" rel="noopener noreferrer">--max-jobs </a>标志?如果达到最大作业数量,工作进程可以退出并释放可能积累的任何内存。为了实现这一点,我们可以在命令中引入一个新选项,如下所示:
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'orders:monitor
{--max-jobs=100 : The number of jobs to process before stopping}';
然后,在我们的handle
方法中,我们可以设置处理的作业初始数量并从我们之前定义的选项中获取最大数量
/**
* Execute the console command.
*/
public function handle()
{
$jobsProcessed = 0;
$maxJobs = $this->option('max-jobs');
while (true) {
// Get all the orders with the pending status
$orders = Order::where('status', 'pending')->get();
一旦作业被发送去处理,我们将增加$jobsProcessed
变量并检查工作进程是否应该退出。如果是这种情况,我们就会返回一个默认的成功退出代码(0
)。在工作进程退出后,Supervisor会重启它。
foreach ($orders as $order) {
// Dispatch the order for further processing to a job
ProcessOrder::dispatch($order)
->onQueue('orders')
->onConnection('database');
// Increase the number of processed jobs
$jobsProcessed++;
// Stop the command if the number of jobs reaches the maximum number set
if($jobsProcessed >= $maxJobs) {
return 0; // Success
}
}
<h2 class="subheading">手动重启命令</h2>当您更新命令中的代码并将其部署到服务器时,只有在Supervisor有机会实际重启工作进程之后,更改才会反映出来。为了解决这个问题,您可以在部署时重启Supervisor本身(如果更新了配置文件,重新读取配置文件)。
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart
更妙的是,我们可以创建另一个自定义命令,以此造成我们的初始化orders:monitor
命令退出,再次允许Supervisor重启它。我们可以通过使用内置的Cache
功能来实现这一点,类似于<a title="Queue重启" href="https://laravel.net.cn/docs/10.x/queues#queue-workers-and-deployment" target="_blank" rel="noopener noreferrer">queue:restart</a>命令那样。
以下是我们的orders:restart
命令的一个示例:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class RestartOrders extends Command
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'orders:restart';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Restart orders monitor command after the current job.';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Cache::forever('orders:restart', true);
}
}
现在,我们需要修改我们的orders:monitor
命令,以检查是否触发了orders:restart
。确保首先包含Cache
门面。
use Illuminate\Support\Facades\Cache;
然后更新循环如下:
foreach ($orders as $order) {
// Dispatch the order for further processing to a job
ProcessOrder::dispatch($order)
->onQueue('orders')
->onConnection('database');
// Increase the number of processed jobs
$jobsProcessed++;
// Stop the command if the number of jobs reaches the maximum number set
if($jobsProcessed >= $maxJobs) {
return 0; // Success
}
// Get the restart command value from the cache
$restartCommand = Cache::get('orders:restart', false);
// Stop the command if needed and remove the entry from the cache
if($restartCommand) {
Cache::forget('orders:restart');
return 0; // Success
}
}
这就完成了。现在您可以将orders:restart
命令添加到您的部署过程中,您就不必担心代码中的任何更改没有反映出来。
正如惯例,您可以通过查看<a title="Gist示例" href="https://gist.github.com/goran-popovic/2aac3715ce25588941e23889b44c88ba" target="_blank" rel="noopener noreferrer nofollow">Gist</a>来获取源代码。
<h2 class="subheading">Supervisor配置</h2>我们还需要做的是,在/etc/supervisor/conf.d
目录(通常是该目录的位置)中添加一个名为,例如monitor-orders.conf
的supervisor配置文件,该目录启动并(如果需要的话)重启我们的命令,它将不断地监控任何待处理的工具订单。
[program:monitor-orders]
process_name=%(program_name)s_%(process_num)02d
command=php /home/forge/app.com/artisan orders:monitor --max-jobs=300
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
user=forge
numprocs=8
redirect_stderr=true
stdout_logfile=/home/forge/app.com/worker.log
stopwaitsecs=3600
上面的设置使用了文档中的一个示例,但您可能不需要生成8个进程(numprocs
),这取决于您收到的订单数量。您的服务器上Artisan命令的路径可能完全不同;可能类似这样php /var/www/my-website.com/artisan
,所以请务必检查这一点以及其他user
值和worker.log
文件路径。
我们做到了。根据queue:work
命令的基本示例和Laravel的文档
中的说明,我们能够创建出自己定制的命令,用于监控我们Handyman Tools应用程序中的待处理订单。借助于Supervisor的帮助,这个命令将在后台自动运行,并在发生任何意外情况时重新启动。
当然,上述示例只是一个起点;你可以围绕它们构建更多的复杂逻辑;你可以引入更多条件来确定待处理订单是否应该被处理;你可以添加其他有用的选项和参数;等等。
总的来说,我希望这篇文章能帮助你更好地理解借助Supervisor运行的Artisan命令。
driesvints, jyllson 喜欢这篇文章