«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Использование Supervisor для обработки выполнения команд Symfony

Использование Supervisor для обработки выполнения команд Symfony

Опубликовано 1 ноября 2024 г.
Просматривать:926

Введение

В этом посте мы узнаем, как использовать супервизор для управления выполнением команды Symfony. По сути, супервизор позволит нам:

  • Автозапуск команды
  • Автоперезапуск команды
  • Укажите количество процессов, которые супервизор должен запустить.

Проблема

Иногда мы прибегаем к unix crontab для автоматизации выполнения процессов. В большинстве случаев это может работать, но могут возникнуть ситуации, когда это может вызвать проблемы.

Предположим, у нас есть таблица базы данных, в которой регистрируются уведомления пользователей. В таблице хранится следующая информация:

  • пользователь
  • текст
  • канал
  • статус (ОЖИДАНИЕ, ОТПРАВЛЕНО)
  • создано в
  • обновлено в

С другой стороны, мы закодировали команду, выполнение которой следует следующим шагам:

  • Запрашивает последние уведомления WAITING
  • Зацикливает запрошенные уведомления и:
    • Отправляет каждое из них соответствующему пользователю.
    • Обновляет статус уведомления с ОЖИДАНИЕ на ОТПРАВЛЕНО

Мы установили эту команду в crontab Linux так, чтобы она запускалась время от времени (1 минута, 2 минуты и т. д.). Все идет нормально.

Теперь представим, что текущий процесс запросил 500 уведомлений, а когда он отправил 400, запускается новый процесс. Это означает, что новый процесс запросит 100 уведомлений, которые еще не были обновлены последним процессом, а также новые:

Using Supervisor to handle a Symfony Command execution

Это может привести к тому, что эти 100 уведомлений будут отправлены дважды, поскольку оба процесса запросили их.

Решение

В качестве решения мы можем прибегнуть к использованию супервизора. Он будет поддерживать работу нашего процесса и перезапустит его при необходимости. Таким образом, мы сохраняем только один процесс и избегаем дублирования. Давайте разберем, как должна выглядеть команда:

#[AsCommand(
    name: 'app:notification'
)]
class NotificationCommand extends Command
{
    private bool $forceFinish = false;

    protected function configure(): void
    {
        $this
            ->addOption('time-limit', null, InputOption::VALUE_OPTIONAL, 'Max time alive in seconds')
            ->addOption('time-between-calls', null, InputOption::VALUE_OPTIONAL, 'Time between every loop call')

        ;
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $this->forceFinish = false;
        pcntl_signal(SIGTERM, [$this, 'signalHandler']);
        pcntl_signal(SIGINT, [$this, 'signalHandler']);

        $timeLimit = $input->getOption('time-limit');
        $timeBetweenCalls = $input->getOption('time-between-calls');
        $dtMax = (new \DateTimeImmutable())->add(\DateInterval::createFromDateString("  {$timeLimit} seconds"));

        do{
           // Here we should execute a service to query and send notifications
           // ......

           sleep($timeBetweenCalls);
           $dtCurrent = new \DateTimeImmutable();

        }while($dtCurrent forceFinish);

        return Command::SUCCESS;
    }

    public function signalHandler(int $signalNumber): void
    {
        echo 'Signal catch: ' . $signalNumber . PHP_EOL;
        match ($signalNumber) {
            SIGTERM, SIGINT => $this->forceFinish = true,
            default => null
        };
    }
}

Давайте объясним команду шаг за шагом:

  • Метод configure объявляет параметры ввода:

    • ограничение по времени: максимальное время, в течение которого командный процесс может быть активным. После этого он завершится, и супервизор перезапустит его.
    • время между вызовами: время сна после каждой итерации цикла. Цикл вызывает службу, которая обрабатывает уведомления, а затем в это время переходит в режим ожидания.
  • Метод execute ведет себя следующим образом:

    • Задает для переменной класса forceFinish значение true
    • Использует библиотеку PHP pnctl для регистрации метода signalHandler для обработки сигналов Unix SIGTERM и SIGINT.
    • Получает значения параметров ввода и вычисляет максимальную дату, в течение которой команда может быть активна до использования значения параметра time-limit.
    • Цикл do- while выполняет необходимый код для получения уведомлений и их отправки (он не помещается в команду, вместо него есть комментарии). Затем он переводит в режим ожидания время, установленное опцией время между вызовами, прежде чем продолжить.
    • Если текущая дата (которая рассчитывается на каждой итерации цикла) меньше максимальной даты и forceFinish имеет значение false, цикл продолжается. В противном случае команда завершается.
  • Функция signalHandler улавливает сигналы SIGTERM и SIGINT Unix. SIGINT — это сигнал, отправляемый, когда мы нажимаем Ctrl C, а SIGTERM — это сигнал по умолчанию, когда мы используем команду kill. Когда функция signalHandler обнаруживает их, она устанавливает для переменной forceFinish значение true, чтобы при завершении текущего цикла команда завершилась, поскольку переменная forceFinish равна true. уже не ложь. Это позволяет пользователям завершить процесс, не дожидаясь завершения максимальной даты.

Настройка супервизора

На данный момент у нас создана команда. Теперь пришло время настроить супервизор, чтобы он мог с этим справиться. Прежде чем начать настройку, мы должны установить супервизор. Вы можете сделать это, выполнив следующую команду:

sudo apt update && sudo apt install supervisor

После установки вы можете убедиться, что супервизор запущен, выполнив следующую команду:

sudo systemctl status supervisor

Файлы конфигурации Supervisor размещаются в следующей папке: /etc/supervisor/conf.d. Давайте создадим файл с именем notif.conf и вставим в него следующее содержимое:

command=php /bin/console app:notifications --time-limit=120 --time-between-calls=10
user=
numprocs=1
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)02d

Давайте объясним каждую клавишу:

  • команда: команда для запуска
  • пользователь: пользователь unix, который запускает команду
  • numprocs: количество запускаемых процессов
  • автозапуск: использовать ли команду автозапуска
  • автозапуск: использовать ли команду автоматического перезапуска
  • имя_процесса: формат имени процесса командной системы Unix.

При такой конфигурации команда app:notifications будет выполняться максимум 120 секунд и переходить в режим сна в течение 10 секунд после каждого цикла. После прохождения 120 секунд или кэширования сигнала unix команда выйдет из цикла и завершится. Затем супервайзер запустит его снова.

Заключение

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

В последней книге, которую я написал, я показываю, как использовать Supervisor, чтобы обеспечить работу рабочих сообщений Symfony. Если вы хотите узнать больше, вы можете найти книгу здесь: Создание ориентированного на операции API с использованием PHP и Symfony Framework: пошаговое руководство

Заявление о выпуске Эта статья воспроизведена по адресу: https://dev.to/icolomina/using-supervisor-to-handle-a-symfony-command-execution-41h7?1. Если есть какие-либо нарушения, свяжитесь с [email protected], чтобы удалить это
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3