Neste post aprenderemos como usar o supervisord para lidar com a execução de um comando symfony. Basicamente, o supervisord nos permitirá:
Às vezes recorremos ao crontab unix para automatizar a execução de processos. Isso pode funcionar na maioria das vezes, mas pode haver situações em que pode causar problemas.
Vamos imaginar que temos uma tabela de banco de dados que registra as notificações dos usuários. A tabela armazena as seguintes informações:
Por outro lado, codificamos um comando cuja execução segue os seguintes passos:
Definimos este comando no crontab do Linux para ser executado de vez em quando (1 minuto, 2 minutos, etc.). Até agora tudo bem.
Agora vamos imaginar que o processo atual consultou 500 notificações e quando enviou 400, um novo processo é iniciado. Isso significa que o novo processo irá consultar as 100 notificações que ainda não foram atualizadas pelo último processo mais as novas:
Isso pode fazer com que essas 100 notificações sejam enviadas duas vezes, pois ambos os processos as consultaram.
Como solução, podemos recorrer ao uso do supervisor. Ele manterá nosso processo em execução e o reiniciará quando necessário. Dessa forma, mantemos apenas um processo e evitamos sobreposições. Vamos analisar como deve ficar o comando:
#[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 }; } }
Vamos explicar o comando passo a passo:
O método configure declara as opções de entrada:
O método execute se comporta da seguinte forma:
A função signalHandler captura os sinais SIGTERM e SIGINT Unix. SIGINT é o sinal enviado quando pressionamos Ctrl C e SIGTERM é o sinal padrão quando usamos o comando kill. Quando a função signalHandler os detecta, ela define a variável forceFinish como true para que, quando o loop atual terminar, o comando termine, pois a variável forceFinish é não é mais falso. Isso permite que os usuários encerre o processo sem ter que esperar até que a data máxima termine.
Até agora, criamos o comando. Agora é hora de configurar o supervisor para que ele possa lidar com isso. Antes de iniciar a configuração, devemos instalar o supervisor. Você pode fazer isso executando o seguinte comando:
sudo apt update && sudo apt install supervisor
Após a instalação, você pode garantir que o supervisor esteja em execução executando o próximo comando:
sudo systemctl status supervisor
Os arquivos de configuração do Supervisor são colocados na seguinte pasta: /etc/supervisor/conf.d. Vamos criar um arquivo chamado notif.conf e colar o seguinte conteúdo:
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
Vamos explicar cada chave:
Com esta configuração, o comando app:notifications ficará rodando por no máximo 120 segundos e ficará suspenso por 10 segundos após cada loop. Depois de passar 120 segundos ou armazenar em cache um sinal unix, o comando sairá do loop e terminará. Em seguida, o supervisor iniciará novamente.
Aprendemos como usar o supervisor para manter um comando em execução sem precisar usar o crontab. Isso pode ser útil quando os processos iniciados pelo crontab podem se sobrepor, causando corrupção de dados.
No último livro que escrevi, mostro como usar o supervisor para manter os trabalhadores do symfony messenger funcionando. Se quiser saber mais, você pode encontrar o livro aqui: Construindo uma API Orientada a Operações usando PHP e o Symfony Framework: Um guia passo a passo
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3