Laravel Artisan CLI безопасно останавливает работников очереди демонов
Чтобы обрабатывать большое количество заданий, я запускаю переменное число рабочих очередей в зависимости от того, какая работа должна завершиться. Я не хочу запускать больше рабочих, чем необходимо для завершения работы, которая должна быть выполнена за определенный промежуток времени, который мы считаем подходящим.
На данный момент я запускаю 5 рабочих очереди демонов для тестирования, однако в производстве это число может составлять от 25 до 100 рабочих, возможно, больше. Я понимаю, что при развертывании мне приходится останавливать рабочих очередей, сначала помещая структуру в режим обслуживания, используя php artisan down
, потому что флаг --daemon
заставляет фреймворк загружаться только тогда, когда рабочий запускается, следовательно, новый код не будет влияют на развертывание, пока рабочий не перезагрузится.
Если мне почему-то нужно было прекратить работу, я мог бы поместить приложение в режим обслуживания с помощью php artisan down
, который заставит рабочих умереть, как только они закончат обработку своего текущего задания (если они работают). Однако могут быть случаи, когда я хочу убить рабочих, не помещая все приложение в режим обслуживания.
Есть ли безопасный способ остановить работников таким образом, чтобы они продолжали обрабатывать свое текущее задание, а затем умереть, не помещая все приложение в режим обслуживания?
По существу, мне нужен php artisan queue:stop
, который ведет себя как php artisan queue:restart
, но не перезагружает рабочего после выполнения задания.
Я делал вывод, чтобы это было как команда php artisan queue:stop
, которая сделала бы это, но это, похоже, не так.
Используя ps aux | grep php
, я могу получить идентификатор процесса для рабочих, и я мог бы убить процессы таким образом, но я не хочу убивать процесс в середине работы, работая над заданием.
Спасибо.
Ответы
Ответ 1
Мы реализовали что-то подобное в нашем приложении - но это не было встроено в Laravel. Вам нужно будет отредактировать этот файл, добавив другое условие в if-блок, чтобы он вызывал функцию stop
. Вы можете сделать это, установив статическую переменную в классе Worker
, которая будет изменяться всякий раз, когда вы запускаете пользовательскую команду, которую вы должны будете сделать (т.е. php artisan queue:pause
), или где-нибудь проверите атомное значение (т.е. установите его в некоторый кеш, такой как redis, memcached, APC или даже MySQL, хотя это будет означать, что у вас будет один запрос MySQL для каждого цикла этого цикла while), который вы используете с помощью той же пользовательской команды.
Ответ 2
При использовании флага --daemon
работники не должны уходить, когда очередь пуста.
Я думаю, что вы ищете в документации для очередей.
Команда php artisan queue:restart
предложит перезапускам работников после выполнения их текущего задания.
Ответ 3
Начиная с Laravel 5.5, есть событие Illuminate\Queue\Events\Looping
которое запускается из daemonShouldRun()
внутри основного рабочего цикла Illuminate\Queue\Worker
. Поэтому, если вы настроите прослушиватель для проверки ваших заданий на обработку и вернете false, то работники очереди остановятся, пока проверка не вернет true. В следующий раз, когда он проверяет его, происходит --sleep <seconds>
который вы можете настроить, передав --sleep <seconds>
команде queue: work.
В настоящее время я использую эту технику во время развертываний, чтобы остановить рабочих, которые работают внутри докерских контейнеров, поскольку не так легко запустить предложенную queue:restart
их без взлома.
Ответ 4
мой Ларавел 5,6. Вы можете (убить свой pid), не волнуйтесь, потеряете свою работу, просто загрузите pcntl (расширение). Laravel может прослушать сигнал и безопасно выйти.
показать источник детали ниже (in./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);
}
ты можешь попытаться убить это