cron скрипт для работы в качестве очереди или очереди для cron? - PullRequest
8 голосов
/ 22 октября 2008

Держу пари, что кто-то уже решил эту проблему, и, возможно, я использую неправильные условия поиска для Google, чтобы сообщить мне ответ, но здесь моя ситуация.

У меня есть скрипт, который я хочу запустить, но я хочу, чтобы он запускался только по расписанию и только по одному за раз. (невозможно запустить скрипт одновременно)

Теперь самое важное: у меня есть таблица myhappyschedule, в которой есть нужные мне данные и запланированное время. Эта таблица может иметь несколько запланированных времен даже в одно и то же время, каждый из которых будет запускать этот сценарий. Поэтому, по сути, мне нужна очередь каждый раз, когда запускается скрипт, и им всем нужно ждать каждого из них, прежде чем он завершится. (иногда выполнение скрипта может занять всего минуту, иногда - много-много минут)

Я думаю о том, чтобы сделать скрипт, который проверяет myhappyschedule каждые 5 минут и собирает те, которые запланированы, помещает их в очередь, где другой скрипт может выполнять каждое «задание» или вхождение в очереди по порядку. Что все это звучит грязно.

Чтобы сделать это дольше - я должен сказать, что я позволяю пользователям планировать события в myhappyschedule, а не редактировать crontab.

Что можно с этим сделать? Блокировки файлов и скрипты, вызывающие скрипты?

Ответы [ 3 ]

4 голосов
/ 22 октября 2008

добавить столбец exec_status к myhappytable (может также time_started и time_finished, см. Псевдокод)

запускать следующий скрипт cron каждые x минут

псевдокод скрипта 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)]

Таким образом, сценарий сначала проверяет, не запущена ли ни одна из команд, затем запускает первую команду, которая еще не выполнена, до тех пор, пока в данный момент не будет выполнено больше команд. Кроме того, вы можете увидеть, какая команда выполняется, выполнив запрос к базе данных.

Потенциальная ловушка: если скрипт cron убит, запланированное задание останется в состоянии «executing_now». Вот для чего нужна блокировка pid в начале и в конце: чтобы убедиться, что скрипт cron завершен правильно. псевдокод создания / проверки 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
2 голосов
/ 22 октября 2008

Вы можете использовать команду at (1) внутри вашего скрипта, чтобы запланировать его следующий запуск. Перед выходом он может проверить myhappyschedule для следующего запуска. Тебе вообще не нужен cron.

0 голосов
/ 08 мая 2009

Я сталкивался с этим вопросом, когда искал решение проблемы с очередями. Для всех, кто ищет здесь, мое решение.

Добавьте к этому cron, который запускает задания по расписанию (даже если они запланированы на одновременное выполнение) и решает проблему, которую вы также описали.

Проблема


  • Должно быть запущено не более одного экземпляра скрипта.
  • Мы хотим отслеживать запросы, чтобы обрабатывать их как можно быстрее.

т. Нам нужен конвейер к скрипту.

Решение:


Создать конвейер для любого скрипта. Закончено с помощью небольшого скрипта bash (далее).

Сценарий можно назвать
./pipeline "<any command and arguments go here>"

Пример: * * тысяча двадцать-пять

./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

Сценарий создает новый именованный канал для каждой команды. Таким образом, приведенное выше создаст именованные каналы: sleep, shabugabu и single_instance_script

В этом случае начальный вызов запустит считыватель и запустит single_instance_script с some arguments в качестве аргументов. Как только вызов завершится, читатель получит следующий запрос с конвейера и выполнит с помощью some other_arguments, завершит, получит следующий и т. Д. ...

Этот скрипт блокирует запрашивающие процессы, поэтому вызывайте его как фоновое задание (и в конце) или как отдельный процесс с 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
...