Maison  >  Article  >  développement back-end  >  Utiliser Supervisor pour gérer l'exécution d'une commande Symfony

Utiliser Supervisor pour gérer l'exécution d'une commande Symfony

WBOY
WBOYoriginal
2024-09-07 06:34:021095parcourir

Introduction

Dans cet article, nous allons apprendre à utiliser superviseur pour gérer l'exécution d'une commande symfony. En gros, superviseur va nous permettre de :

  • Démarrer automatiquement la commande
  • Redémarrer automatiquement la commande
  • Spécifiez le nombre de processus que nous souhaitons que le superviseur démarre.

Le problème

Parfois, nous avons recours à la crontab Unix pour automatiser l'exécution des processus. Cela peut fonctionner la plupart du temps, mais il peut y avoir des situations où cela peut causer des problèmes.

Imaginons que nous ayons une table de base de données qui enregistre les notifications des utilisateurs. Le tableau stocke les informations suivantes :

  • utilisateur
  • texte
  • chaîne
  • statut (EN ATTENTE, ENVOYÉ)
  • créé à
  • mis à jourà

D'autre part, nous avons codé une commande dont l'exécution suit les étapes suivantes :

  • Interroge les dernières notifications en ATTENTE
  • Boucle les notifications interrogées et :
    • Envoie chacun à l'utilisateur correspondant.
    • Met à jour l'état de la notification de ATTENTE à ENVOYÉ

Nous avons défini cette commande dans la crontab Linux pour qu'elle s'exécute de temps en temps (1 minute, 2 minutes, etc.). Jusqu’ici tout va bien.

Imaginons maintenant que le processus actuel ait interrogé 500 notifications et qu'après en avoir envoyé 400, un nouveau processus démarre. Cela signifie que le nouveau processus interrogera les 100 notifications qui n'ont pas encore été mises à jour par le dernier processus ainsi que les nouvelles :

Using Supervisor to handle a Symfony Command execution

Cela peut entraîner l'envoi de ces 100 notifications deux fois puisque les deux processus les ont interrogées.

La solution

Comme solution, nous pouvons recourir à l'utilisation d'un superviseur. Il maintiendra notre processus en cours et le redémarrera si nécessaire. De cette façon, nous ne conservons qu’un seul processus et évitons les chevauchements. Analysons à quoi devrait ressembler la commande :

#[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 < $dtMax && !$this->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
        };
    }
}

Expliquons la commande étape par étape :

  • La méthode configure déclare aux options de saisie :

    • délai : durée maximale pendant laquelle le processus de commande peut être actif. Après cela, il se terminera et le superviseur le redémarrera.
    • temps entre-appels : temps de sommeil après chaque itération de boucle. La boucle appelle le service qui traite les notifications puis se met en veille pendant cette période.
  • La méthode execute se comporte comme suit :

    • Définit la variable de classe forceFinish sur true
    • Utilise la bibliothèque PHP pnctl pour enregistrer la méthode signalHandler pour gérer les signaux Unix SIGTERM et SIGINT.
    • Obtient les valeurs des options d'entrée et calcule la date maximale à laquelle la commande peut être active jusqu'à ce que la valeur de l'option time-limit soit utilisée.
    • La boucle do-while exécute le code requis pour obtenir les notifications et les envoyer (elle n'est pas placée dans la commande, il y a des commentaires à la place). Ensuite, il met en veille le temps établi par l'option délai entre les appels avant de continuer.
    • Si la date actuelle (qui est calculée à chaque itération de boucle) est inférieure à la date maximale et que la forceFinish est fausse, la boucle continue. Sinon la commande se termine.
  • La fonction signalHandler capte les signaux SIGTERM et SIGINT Unix. SIGINT est le signal envoyé lorsque nous appuyons sur Ctrl+C et SIGTERM est le signal par défaut lorsque nous utilisons la commande kill. Lorsque la fonction signalHandler les détecte, elle définit la variable forceFinish sur true afin que, lorsque la boucle en cours se termine, la commande se termine puisque la variable forceFinish est n'est plus faux. Cela permet aux utilisateurs de terminer le processus sans avoir à attendre que la date maximale soit terminée.

Configuration du superviseur

Jusqu'à présent, nous avons créé la commande. Il est maintenant temps de configurer le superviseur pour qu'il puisse le gérer. Avant de commencer la configuration, nous devons installer le superviseur. Vous pouvez le faire en exécutant la commande suivante :

sudo apt update && sudo apt install supervisor

Après l'installation, vous pouvez vous assurer que le superviseur est en cours d'exécution en exécutant la commande suivante :

sudo systemctl status supervisor

Les fichiers de configuration du superviseur sont placés dans le dossier suivant : /etc/supervisor/conf.d. Créons un fichier nommé notif.conf et collons le contenu suivant :

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

Expliquons chaque clé :

  • commande : La commande pour démarrer
  • utilisateur : L'utilisateur Unix qui exécute la commande
  • numprocs : Le nombre de processus à exécuter
  • démarrage automatique : s'il faut démarrer automatiquement la commande
  • démarrage automatique : s'il faut redémarrer automatiquement la commande
  • process_name : Le format du nom du processus Unix de la commande.

Avec cette configuration, la commande app:notifications s'exécutera pendant un maximum de 120 secondes et elle dormira pendant 10 secondes après chaque boucle. Après avoir passé 120 secondes ou mis en cache un signal Unix, la commande quittera la boucle et se terminera. Ensuite, le superviseur le redémarrera.

Conclusion

Nous avons appris à utiliser le superviseur pour maintenir une commande en cours d'exécution sans avoir à utiliser la crontab. Cela peut être utile lorsque les processus lancés par la crontab peuvent se chevaucher, provoquant une corruption des données.

Dans le dernier livre que j'ai écrit, je montre comment utiliser le superviseur pour que les travailleurs de messagerie Symfony continuent de fonctionner. Si vous souhaitez en savoir plus, vous pouvez trouver le livre ici : Construire une API orientée opérations à l'aide de PHP et du framework Symfony : un guide étape par étape

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn