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
Я могу получить идентификаторы процессов для рабочих, и я мог бы убить процессы таким образом, но я не хочу убивать процесс в середине его работы на работе.
Благодарю.
5 ответов
Мы реализовали нечто подобное в нашем приложении, но это не было встроено в сам Laravel. Вам нужно будет отредактировать этот файл, добавив еще одно условие в блок if, чтобы он вызывал stop
функция. Вы можете сделать это, установив статическую переменную в Worker
класс, который изменяется каждый раз, когда вы запускаете пользовательскую команду, которую вам нужно сделать (т.е. php artisan queue:pause
) или путем проверки атомарного значения где-то (т.е. установить его в некотором кэше, таком как redis, memcached, APC или даже MySQL, хотя это будет означать, что у вас будет один запрос MySQL для каждого цикла этого цикла while), который вы устанавливаете, используя та же самая пользовательская команда.
При использовании --daemon
работники флага не должны выходить, когда очередь пуста.
Я думаю, что вы ищете в документации для очередей.
php artisan queue:restart
Команда предложит работникам перезагрузиться после того, как они выполнят свою текущую работу.
Начиная с Laravel 5.5 существует событие под названием Illuminate\Queue\Events\Looping
который уволен из daemonShouldRun()
вызов внутри основного рабочего цикла Illuminate\Queue\Worker
, Поэтому, если вы настроите прослушиватель для проверки ваших заданий на обработку и вернете false, то работники очереди остановятся, пока проверка не вернет true. Между следующей проверкой будет сон, который вы можете настроить, передав --sleep <seconds>
в очередь: рабочая команда.
В настоящее время я использую эту технику во время развертываний, чтобы остановить рабочих, которые работают внутри докерских контейнеров, поскольку не так легко запустить предложенный queue:restart
на них без взлома.
Мой Laravel - 5.6. вы можете (убить свой pid) не волнуйтесь, потеряйте свою работу, просто загрузите pcntl(расширение) 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);
}
ты можешь попытаться убить его
также - if (something=something)? app('queue.worker')->shouldQuit = 1;
работает хорошо
Это также действительно хорошо для работ, которые засасывают барана, поскольку он убивает процесс, а затем ваш горизонт или что-то еще вернет его обратно.