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

ты можешь попытаться убить это