Laravel Artisan CLI 安全地停止守护进程队列工作者

Laravel Artisan CLI safely stop daemon queue workers

为了处理大量作业,我 运行 根据要完成的工作量,使用可变数量的队列工作程序。我不想 运行 多于完成需要在我们认为合适的时间段内完成的工作所需的工人。

目前,出于测试目的,我启动了 5 个守护进程队列工作人员,但在生产中,这个数字可能在 25 到 100 个工作人员之间,可能更多。我知道在部署时,我必须首先使用 php artisan down 将框架置于维护模式来停止队列工作人员,因为 --daemon 标志导致框架仅在工作人员启动时加载,因此在工作人员重新启动之前,新代码不会在部署期间生效。

如果我出于某种原因需要停止工作人员,我可以使用 php artisan down 将应用程序置于维护模式,这将导致工作人员在完成当前工作后死亡(如果他们正在工作一)。但是,有时我可能想在不将整个应用程序置于维护模式的情况下杀死工作人员。

有没有一种安全的方法可以停止工作人员,让他们继续处理当前的工作,然后在不将整个应用程序置于维护模式的情况下死亡?

本质上我需要的是一个php artisan queue:stop,它的行为类似于php artisan queue:restart,但一旦工作完成就不会重启worker。

我原以为会有类似 php artisan queue:stop 的命令可以执行此操作,但事实并非如此。

使用 ps aux | grep php 我能够获得工作人员的进程 ID,并且我可以那样终止进程,但我不想在它正在处理的过程中终止进程工作。

谢谢。

使用 --daemon 标志时,工作人员不应在队列为空时退出。

我想你要找的是in the documentation for queues.

php artisan queue:restart 命令将提示工作人员在完成当前工作后重新启动。

我们已经在我们的应用程序中实现了类似的功能 - 但它并不是 Laravel 本身的内置功能。您必须通过向 if 块添加另一个条件来编辑 this file,以便它调用 stop 函数。您可以通过在 Worker class 中设置一个静态变量来做到这一点,每当您 运行 您必须执行的自定义命令(即 php artisan queue:pause)或者通过检查某处的原子值(即将其设置在某些缓存中,如 redis、memcached、APC 甚至 MySQL,尽管这意味着你将对这个 while 的每个周期进行一个 MySQL 查询 -循环),您使用相同的自定义命令设置。

因为 Laravel 5.5 有一个名为 Illuminate\Queue\Events\Looping 的事件,它从 Illuminate\Queue\Worker 的主工作循环内的 daemonShouldRun() 调用中触发。因此,如果您设置一个侦听器来执行 should process jobs 检查,并且 return false 那么队列工作人员将停止,直到检查 returns 为真.在下次检查之间有一个睡眠,您可以通过将 --sleep <seconds> 传递给 queue:work 命令来自定义。

我目前在部署期间使用此技术来停止 运行 在 docker 容器内的工作人员,因为要 运行 建议的 queue:restart 并不容易他们没有四处乱逛。

我的 Laravel 是 5.6。 你可以(杀死你的 pid)不用担心失去你的工作 只需加载 pcntl(extension) Laravel 即可收听信号并安全退出

在下面显示部分源代码(在 ./vendor/laravel/framework/src/Illuminate/Queue/Worker.php):

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;
    });
}

下面是我的测试:

for($i=0; $i<100; $i++){
        pcntl_async_signals(true);

    pcntl_signal(SIGTERM, function () {
        echo 'SIGTERM';
    });

    pcntl_signal(SIGUSR2, function () {
        echo 'SIGUSR2';
    });

    pcntl_signal(SIGCONT, function () {
        echo 'SIGCONT';
    });
echo $i; 
sleep(1);
 }

你可以尝试杀掉它

还有 - 如果(某事=某事)?应用程序('queue.worker')->shouldQuit = 1;

效果不错

它也非常适合吸收 ram 的工作,因为它会终止进程,然后你的 horizon 或任何将它踢回来的东西。