Comment exécuter cron plus d'une fois par minute en utilisant PHP

Le fichier de configuration classique pour les entrées de tâches cron dans le système d'exploitation Linux est le suivant:

# ┌─────────────  (0 - 59)
# │ ┌─────────────  (0 - 23)
# │ │ ┌─────────────  (1 - 31)
# │ │ │ ┌─────────────  (1 - 12)
# │ │ │ │ ┌─────────────   (0 - 6)
# │ │ │ │ │
# * * * * * <  >

Les cinq premiers paramètres indiquent le temps d'exécution de cette tâche, et le sixième - la commande elle-même, qui doit être exécutée. Les paramètres horaires sont: minutes, heures, jours, mois et jour de la semaine. De plus, tous les nombres doivent être représentés sous forme d'entiers ou sous forme de syntaxe spéciale .

Par conséquent, l'intervalle minimum possible pour lancer une commande est une fois par minute.

Pour de nombreuses tâches, l'exécution des commandes est nécessaire beaucoup plus souvent, par exemple toutes les 10 secondes. Pour certaines tâches d'automatisation des processus métier, le délai maximal autorisé n'est souvent pas supérieur à 1 à 1,5 seconde.

Bien sûr, le cron classique ne convient pas à cela - il doit être amélioré.

Ce qui suit est une implémentation pas à pas de la création de fonctionnalités supplémentaires (en PHP) au cron classique sous Linux en utilisant une protection supplémentaire contre les processus de redémarrage.

Configuration des tâches et configuration cron


Par exemple, nous utiliserons la tâche suivante:

  1. En frontal, l'utilisateur peut lancer l'exécution d'une tâche complexe en cliquant sur le bouton "Exécuter";
  2. Le serveur principal après avoir écrit une nouvelle ligne dans la base de données informe l'utilisateur de la confirmation;
  3. Grâce à cron, nous allons «suivre» ces nouvelles tâches et les terminer le plus rapidement possible afin que l'utilisateur n'obtienne pas le résultat en une minute, mais immédiatement *.

* Si vous utilisez le lancement des commandes, juste par minute, alors la tâche commencera lorsque les secondes atteindront le zéro le plus proche (le début d'une nouvelle minute). Par conséquent, dans la forme classique, l'utilisateur devra attendre l'exécution de 0 à 59 secondes.

Ainsi, cron sera configuré dans sa forme maximale, c'est-à-dire une fois par minute:

* * * * * /usr/bin/php -q /run.php > /dev/null 2>&1
#/usr/bin/php -    PHP   (      )
#/run.php -   PHP   
#> /dev/null 2>&1 - ,            run.php

Cycle unique


Initialement, vous devez décider de la fréquence à laquelle nous demanderons de nouvelles tâches dans la base de données - en fonction de cela, le nombre de cycles et la veille logique (fonction sleep) changeront.

Dans l'exemple actuel, une étape de 10 secondes est utilisée. Par conséquent, le nombre de cycles est de 60/10 = 6. Total, le code général est le suivant:

for ($cycle = 1; $cycle <= 6; $cycle++) { # 6 
    $all_tasks = get_all_tasks(); #     
    if ($all_tasks) { #    - 
        foreach($all_tasks as $one_task) { #     
            solve_one_task($one_task); #  
        }
    }
    sleep(10); #  ,  ""  10 
}

Clarification : dans cet exemple, une étape de 10 secondes est utilisée, ce qui peut fournir un intervalle minimum d'exécution de script une fois toutes les 10 secondes. Par conséquent, pour une exécution plus fréquente, vous devez modifier le nombre de cycles et le "temps de sommeil".

Éviter l'exécution répétée de tâches


Dans la forme présentée, il y a une incertitude, à savoir l'exécution répétée de la tâche si elle a déjà été lancée. Cela devient particulièrement vrai si la tâche est "difficile" et nécessite quelques secondes pour la mettre en œuvre.

Cela crée un problème de réexécution:

  • La fonction est solve_one_task()dĂ©jĂ  en cours d'exĂ©cution, mais n'a pas encore terminĂ© son travail;
  • Par consĂ©quent, dans la base de donnĂ©es, la tâche est toujours marquĂ©e comme non exĂ©cutĂ©e;
  • Le cycle suivant rĂ©cupĂ©rera cette tâche et exĂ©cutera Ă  solve_one_task()nouveau la fonction , avec la mĂŞme tâche.

Bien sûr, cela peut être résolu, par exemple, en modifiant un état dans la base de données pour cette tâche.

Mais nous ne chargerons pas la base de données: sur la base de mes tests, MYSQL peut accepter la demande, mais pas la traiter immédiatement. Une différence de 0,5 seconde peut entraîner des exécutions répétées, ce qui n'est absolument pas approprié.

Dans ce cas également, nous ne parlons que de l'état des tâches, il est donc préférable d'utiliser le système de fichiers du serveur.

Le modèle de vérification de base est construit à l'aide de flock- une fonction qui définit et déverrouille un fichier.

En exécution PHP, la fonction peut être représentée comme suit:

$lock_file_abs = 'file'; #  
$fp = fopen($lock_file_abs,"w+"); #   
if (flock($fp, LOCK_EX | LOCK_NB)) { #,    
    solve_one_task($one_task); #  
    flock($fp, LOCK_UN); #   ,     
}
else {
    #,   , ..       
}
fclose($fp); #   
unlink($lock_file_abs); # ,   

RĂ©sultat


La vue générale de l'ensemble du cycle est la suivante:

for ($cycle = 1; $cycle <= 6; $cycle++) {
    $all_tasks = get_all_tasks();
    if ($all_tasks) {
        foreach($all_tasks as $one_task) {
            $lock_file_abs = __DIR__.'/locks/run_'.$one_task['id'];
            $fp = fopen($lock_file_abs,"w+");
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                solve_one_task($one_task);
                flock($fp, LOCK_UN);
            }
            else {
                #    
            }
            fclose($fp);
            unlink($lock_file_abs);
        }
    }
    sleep(10);
}

Ainsi, un tel algorithme vous permet de démarrer une exécution cyclique du traitement des tâches et ne craint pas que la tâche soit traitée plus d'une fois.

Et maintenant, il suffit d'exécuter ce code via cron toutes les minutes, et à son tour, il exécutera des cycles plus petits déjà à l'intérieur de lui-même.

All Articles