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;

работает хорошо

Это также действительно хорошо для работ, которые засасывают барана, поскольку он убивает процесс, а затем ваш горизонт или что-то еще вернет его обратно.

Другие вопросы по тегам