Cron-Skript als eine Warteschlange oder eine Warteschlange für cron zu handeln?
Frage
Ich wette, dass jemand dies bereits gelöst hat und vielleicht bin ich mit den falschen Suchbegriffe für Google mir die Antwort zu sagen, aber hier ist meine Situation.
Ich habe ein Skript, das ich laufen soll, aber ich will es nur ausgeführt werden, wenn geplant und nur einen nach dem anderen. (Kann das Skript nicht gleichzeitig ausgeführt werden)
Nun ist der klebrige Teil ist, dass sagen, ich habe eine Tabelle „myhappyschedule“ genannt, die die Daten, die ich brauche und die geplante Zeit. Diese Tabelle kann mehrere geplanten Termine sogar zur gleichen Zeit hat, jeder würde dieses Skript ausführen. So im Wesentlichen brauche ich eine Warteschlange jedes Mal, wenn die Skript Feuer und sie müssen alle für jeden warten, bevor es zu beenden. (Manchmal kann dies nur eine Minute dauern, bis das Skript manchmal seine viele, viele Minuten auszuführen)
Was ich denke über das Tun ein Skript macht die myhappyschedule alle 5 Minuten überprüft und diejenigen einsammelt, die geplant sind, legt sie in eine Warteschlange, wo ein anderes Skript jeden ‚Job‘ ausführen kann, oder das Auftreten in der Warteschlange um. Was all dies klingt chaotisch.
Um dies zu mehr machen - ich würde sagen, dass ich damit die Benutzer der Dinge in myhappyschedule planen und nicht crontab bearbeiten.
Was kann dagegen unternommen werden? Dateisperren und Skripte Aufruf Skripte?
Lösung
fügen Sie eine Spalte exec_status
myhappytable
(vielleicht auch time_started
und time_finished
finden Pseudo-Code)
Führen Sie den folgenden Cron-Skript alle x Minuten
Pseudo-Code von cron-Skript:
[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)]
Auf diese Weise kann das Skript zunächst überprüft, ob keine der Befehle ausgeführt wird, dann läuft zunächst noch nicht Befehl ausführen, bis es nicht mehr Befehle sind im gegebenen Augenblick ausgeführt werden. Außerdem können Sie sehen, was Befehl ausführt Abfragen der Datenbank.
Ein potentielles pitfall: , wenn der Cron-Skript getötet wird, eine geplante Aufgabe wird in "executing_now" Zustand verbleibt. Das ist, was die pid Sperre am Anfang und Ende ist: um zu sehen, ob der Cron-Skript ordnungsgemäß beendet. Pseudo-Code von erstellen / Check 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
Andere Tipps
Sie können die unter (1) Befehl in Ihrem Skript verwenden, um seinen nächsten Lauf zu planen. Bevor es verlässt, kann es myhappyschedule für die nächste Laufzeit überprüfen. Sie brauchen nicht cron überhaupt, wirklich.
ich auf diese Frage kam, während eine Lösung für das Warteschlangen-Problem untersucht. Zum Wohl sonst jemand hier suchen ist meine Lösung.
In Kombination mit einem cron, die Arbeitsplätze beginnt, wie sie geplant sind (auch wenn sie geplant sind, die gleichzeitig ausgeführt werden) und das löst das Problem, das Sie als gut beschrieben.
Problem
- Allenfalls eine Instanz des Skripts sollte ausgeführt werden.
- Wir wollen Anfragen spulen, um sie so schnell wie möglich zu bearbeiten.
dh. Wir brauchen eine Pipeline an das Skript.
Lösung:
eine Pipeline zu jedem Skript erstellen. Geschah einen kleinen Bash-Skript (weiter unten).
Das Skript kann als
genannt werden
./pipeline "<any command and arguments go here>"
Beispiel:
./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
Das Skript erstellt eine neue Named Pipe für jeden Befehl. So die oben wird Named Pipes create: sleep
, shabugabu
und single_instance_script
In diesem Fall wird der erste Aufruf wird ein Lesegerät und Lauf single_instance_script
mit some arguments
als Argumente starten. Sobald der Anruf beendet ist, wird der Leser die nächste Anforderung aus dem Rohr greifen und führt mit some other_arguments
, komplett, greift die nächste etc ...
Dieses Skript blockiert Prozesse anfordert, so dass es als Hintergrundjob nennen (& am Ende) oder als freistehenden Prozess mit at
(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