cron script per fungere da coda O da coda per cron?
Domanda
Scommetto che qualcuno ha già risolto questo problema e forse sto usando termini di ricerca errati per google per dirmi la risposta, ma ecco la mia situazione.
Ho uno script che voglio eseguire, ma voglio che venga eseguito solo quando pianificato e solo uno alla volta. (impossibile eseguire lo script contemporaneamente)
Ora la parte appiccicosa è che dire che ho una tabella chiamata " myhappyschedule " che ha i dati di cui ho bisogno e l'orario programmato. Questa tabella può avere più orari pianificati anche contemporaneamente, ognuno eseguirà questo script. Quindi, in sostanza, ho bisogno di una coda ogni volta che lo script viene attivato e tutti devono attendere che ciascuno prima che finisca. (a volte questo può richiedere solo un minuto per l'esecuzione dello script a volte i suoi molti molti minuti)
Quello che sto pensando di fare è creare uno script che controlla myhappyschedule ogni 5 minuti e raccoglie quelli programmati, inserendoli in una coda in cui un altro script può eseguire ogni 'lavoro' o occorrenza nella coda in ordine. Che tutto ciò suona disordinato.
Per allungare il tempo, dovrei dire che sto permettendo agli utenti di pianificare cose in myhappyschedule e non modificare crontab.
Cosa si può fare al riguardo? Blocchi di file e script che chiamano script?
Soluzione
aggiungi una colonna exec_status
a myhappytable
(forse anche time_started
e time_finished
, vedi pseudocodice)
esegui il seguente cron script ogni x minuti
pseudocodice di cron script:
[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)]
In questo modo, lo script controlla innanzitutto se nessuno dei comandi è in esecuzione, quindi esegue il primo comando non ancora eseguito, fino a quando non ci sono più comandi da eseguire in un determinato momento. Inoltre, puoi vedere quale comando sta eseguendo interrogando il database.
Un potenziale trabocchetto: se lo script cron viene ucciso, un'attività pianificata rimarrà tra " execing_now " stato. Ecco a cosa serve il blocco pid all'inizio e alla fine: per vedere se lo script cron è terminato correttamente. pseudocodice di create / 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
Altri suggerimenti
È possibile utilizzare il comando at (1) all'interno dello script per pianificare la sua prossima esecuzione. Prima di uscire, può controllare myhappyschedule per il prossimo periodo di esecuzione. In realtà non hai bisogno di cron.
Mi sono imbattuto in questa domanda mentre cercavo una soluzione al problema delle code. A beneficio di chiunque altro cerchi qui è la mia soluzione.
Combina questo con un cron che avvia i lavori come pianificato (anche se sono pianificati per essere eseguiti contemporaneamente) e che risolve anche il problema che hai descritto.
Problema
- Almeno un'istanza dello script dovrebbe essere in esecuzione.
- Vogliamo fornire richieste per elaborarle il più rapidamente possibile.
es. Abbiamo bisogno di una pipeline per lo script.
Soluzione:
Crea una pipeline per qualsiasi script. Fatto usando un piccolo script bash (più in basso).
Lo script può essere chiamato come
./pipeline "<any command and arguments go here>"
Esempio:
./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
Lo script crea un nuovo named pipe per ciascuno comando. Quindi quanto sopra creerà le pipe denominate: sleep
, shabugabu
e single_instance_script
In questo caso la chiamata iniziale avvierà un lettore ed eseguirà some arguments
con some other_arguments
come argomenti. Una volta completata la chiamata, il lettore prenderà la richiesta successiva dalla pipe ed eseguirà con at
, completerà, afferrerà la successiva ecc ...
Questo script bloccherà i processi richiedenti, quindi chiamalo come processo in background (& amp; alla fine) o come processo distaccato con 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