сценарий cron, который будет действовать как очередь ИЛИ очередь для cron?

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

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

добавить столбец 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

Другие советы

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

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

Объедините это с 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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top