script cron pour agir comme une file d'attente OU une file d'attente pour cron?

StackOverflow https://stackoverflow.com/questions/226473

  •  03-07-2019
  •  | 
  •  

Question

Je parie que quelqu'un a déjà résolu ce problème et peut-être que j'utilise les mauvais termes de recherche pour que Google me donne la réponse, mais voici ma situation.

J'ai un script que je veux exécuter, mais je veux qu'il ne soit exécuté que lorsque programmé et seulement un à la fois. (impossible d'exécuter le script simultanément)

Maintenant, la partie collante est que disons que j'ai une table appelée & "myhappyschedule &"; qui a les données dont j'ai besoin et l'heure prévue. Cette table peut avoir plusieurs heures planifiées même en même temps, chacune exécutera ce script. Donc, j'ai essentiellement besoin d'une file d'attente à chaque fois que le script est lancé et ils doivent tous attendre que chacun d'entre eux se termine. (Parfois, cela peut prendre une minute pour que le script exécute parfois ses nombreuses minutes)

Ce que je songe à faire, c'est de créer un script qui vérifie mon programme toutes les 5 minutes et rassemble ceux qui sont planifiés, les place dans une file d'attente dans laquelle un autre script peut exécuter chaque "tâche" ou occurrence de la file d'attente dans l'ordre. Ce qui semble tout en désordre.

Pour prolonger cela - je devrais dire que je permets aux utilisateurs de planifier des choses dans monhappyschedule et de ne pas éditer la crontab.

Que peut-on faire à ce sujet? Verrous de fichiers et scripts d’appel de scripts?

Était-ce utile?

La solution

ajoutez une colonne exec_status à myhappytable (peut-être aussi time_started et time_finished, voir pseudocode)

exécuter le script cron suivant toutes les x minutes

pseudocode du script cron:

[create/check pid lock (optional, but see "A potential pitfall" below)]
get number of rows from myhappytable where (exec_status == executing_now)
if it is > 0, exit
begin loop
  get one row from myhappytable
    where (exec_status == not_yet_run) and (scheduled_time <= now)
    order by scheduled_time asc
  if no such row, exit
  set row exec_status to executing_now (maybe set time_started to now)
  execute whatever command the row contains
  set row exec_status to completed
  (maybe also store the command output/return as well, set time_finished to now)
end loop
[delete pid lock file (complementary to the starting pid lock check)]

Ainsi, le script vérifie d’abord si aucune des commandes n’est en cours d’exécution, puis exécute la première commande non encore exécutée, jusqu’à ce qu’il n’y ait plus de commandes à exécuter au moment donné. Vous pouvez également voir quelle commande est en cours d’exécution en interrogeant la base de données.

Un piège potentiel: si le script cron est tué, une tâche planifiée restera dans & "executing_now &"; Etat. C’est à cela que sert le verrou pid au début et à la fin: voir si le script cron s’est terminé correctement. pseudocode de création / vérification de pidlock:

if exists pidlockfile then
  check if process id given in file exists
  if not exists then
    update myhappytable set exec_status = error_cronscript_died_while_executing_this   
      where exec_status == executing_now
    delete pidlockfile
  else (previous instance still running)
    exit
  endif
endif
create pidlockfile containing cron script process id

Autres conseils

Vous pouvez utiliser la commande at (1) dans votre script pour planifier sa prochaine exécution. Avant de se terminer, il peut vérifier myhappyschedule pour la prochaine exécution. Vous n'avez vraiment pas besoin de cron.

Je suis tombé sur cette question alors que je cherchais une solution au problème des files d’attente. Pour le bénéfice de tous ceux qui cherchent ici, voici ma solution.

Combinez ceci avec un cron qui démarre les tâches telles qu'elles sont planifiées (même si leur exécution est planifiée simultanément) et qui résout également le problème que vous avez décrit.

Problème

  • Au plus une instance du script doit être en cours d'exécution.
  • Nous voulons que les requêtes soient traitées afin qu'elles soient traitées le plus rapidement possible.

c'est à dire. Nous avons besoin d’un pipeline vers le script.

Solution:

Créez un pipeline vers n’importe quel script. Fait en utilisant un petit script bash (plus bas).

Le script peut être appelé comme
./pipeline "<any command and arguments go here>"

Exemple:

./pipeline sleep 10 &
./pipeline shabugabu &
./pipeline single_instance_script some arguments &
./pipeline single_instance_script some other_argumnts &
./pipeline "single_instance_script some yet_other_arguments > output.txt" &
..etc

Le script crée un canal nommé pour chaque commander. Donc, ce qui précède créera des canaux nommés: sleep, shabugabu et single_instance_script

.

Dans ce cas, l'appel initial démarrera un lecteur et exécutera some arguments avec some other_arguments comme arguments. Une fois l’appel terminé, le lecteur saisira la prochaine requête du tube et s’exécutera avec at, complétera, récupérera la suivante, etc. ...

Ce script bloquant les processus demandeurs, appelez-le comme tâche en arrière-plan (&; à la fin) ou comme processus détaché avec at now <<< "./pipeline some_script" (<=>)

#!/bin/bash -Eue

# Using command name as the pipeline name
pipeline=$(basename $(expr "$1" : '\(^[^[:space:]]*\)')).pipe
is_reader=false

function _pipeline_cleanup {
        if $is_reader; then
                rm -f $pipeline
        fi
        rm -f $pipeline.lock

        exit
}
trap _pipeline_cleanup INT TERM EXIT

# Dispatch/initialization section, critical
lockfile $pipeline.lock
        if [[ -p $pipeline ]]
        then
                echo "$*" > $pipeline
                exit
        fi

        is_reader=true
        mkfifo $pipeline
        echo "$*" > $pipeline &
rm -f $pipeline.lock

# Reader section
while read command < $pipeline
do
        echo "$(date) - Executing $command"
        ($command) &> /dev/null
done
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top