Универсальное решение для предотвращения параллельного запуска длинных заданий cron?

27

Я ищу простое и универсальное решение, которое позволило бы вам выполнить любой скрипт или приложение в crontab и предотвратить его запуск дважды.

Решение должно быть независимым от выполняемой команды.

Я предполагаю, что это должно выглядеть так, lock && (command ; unlock)где lock вернет false, если была другая блокировка.

Вторая часть была бы похожа на получение блокировки, запуска команды и разблокировки после выполнения команды, даже если она возвращает ошибку.

Сорин
источник

Ответы:

33

Посмотрите на пакет run-oneУстановите run-one . С man-страницы для run-oneкомандыЗначок Manpage :

run-one - это скрипт-обертка, который запускает не более одного уникального экземпляра некоторой команды с уникальным набором аргументов.

Это часто полезно с cronjobs, когда вы хотите, чтобы одновременно работало не более одной копии.

Как timeили sudo, вы просто добавляете это к команде. Таким образом, cronjob может выглядеть так:

  */60 * * * *   run-one rsync -azP $HOME example.com:/srv/backup

Для получения дополнительной информации и справки, проверьте сообщение в блоге, представляющее его Дастин Киркланд.

andrewsomething
источник
5

Очень простой способ урегулирования блокировки:

if mkdir /var/lock/mylock; then
  echo "Locking succeeded" >&2
else
  echo "Lock failed - exit" >&2
  exit 1
fi

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

Для получения дополнительной информации о замках bash, проверьте эту страницу

OrangeTux
источник
1
Вам также понадобится ловушка EXIT, которая удаляет блокировку при выходе. echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
гейра
1
В идеале вы хотите использовать консультативное стадо из процесса, который выполняет команду, которую вы хотите использовать в качестве подзадачи. Таким образом , если все они умирают стадо освобождается автоматически, что , используя наличие файла блокировки не делает. Использование сетевого порта будет работать аналогичным образом - хотя это намного меньше пространства имен, что является проблемой.
Алекс Норт-Киз
3

Не нужно устанавливать какой-нибудь модный пакет:

#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"

Этот сценарий быстрее написать самому, чем запускать "apt-get install", не так ли? Возможно, вы захотите добавить «-u $ (id -u)» в pgrep, чтобы проверять экземпляры, запускаемые только текущим пользователем.

Майкл Коухан
источник
2
это не гарантирует ни одного случая. два сценария могут одновременно передаваться на другую сторону ||оператора, прежде чем любой из них еще сможет запустить сценарий.
Седат Капаноглу
@SedatKapanoglu Да, этот сценарий не защищен от условий гонки, но исходный вопрос касался длительных заданий cron (которые запускаются не чаще одного раза в минуту). Если вашей системе требуется больше минуты для создания процесса, у вас есть другие проблемы. Однако, если это необходимо по каким-то другим причинам, вы можете использовать flock (1) для защиты вышеуказанного скрипта от условий гонки.
Майкл Коухан
Я использовал это, но для bash-скрипта, который должен проверить себя. Код такой: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && exit 2
ahofmann
3

См. Также Tim Kay's solo, который выполняет блокировку путем привязки порта к петлевому адресу, уникальному для пользователя:

http://timkay.com/solo/

В случае, если его сайт закрывается:

Использование:

solo -port=PORT COMMAND

where
    PORT        some arbitrary port number to be used for locking
    COMMAND     shell command to run

options
    -verbose    be verbose
    -silent     be silent

Используйте это так:

* * * * * solo -port=3801 ./job.pl blah blah

Автор сценария:

#!/usr/bin/perl -s
#
# solo v1.7
# Prevents multiple cron instances from running simultaneously.
#
# Copyright 2007-2016 Timothy Kay
# http://timkay.com/solo/
#
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation;
#    either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option)
#    any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or
#
# b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or
#
# c) the MIT License (http://opensource.org/licenses/MIT)
#

use Socket;

alarm $timeout                              if $timeout;

$port =~ /^\d+$/ or $noport                     or die "Usage: $0 -port=PORT COMMAND\n";

if ($port)
{
    # To work with OpenBSD: change to
    # $addr = pack(CnC, 127, 0, 1);
    # but make sure to use different ports across different users.
    # (Thanks to  www.gotati.com .)
    $addr = pack(CnC, 127, $<, 1);
    print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n"   if $verbose;

    $^F = 10;           # unset close-on-exec

    socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp'))       or die "socket: $!";
    bind(SOLO, sockaddr_in($port, $addr))               or $silent? exit: die "solo($port): $!\n";
}

sleep $sleep if $sleep;

exec @ARGV;
ДНК
источник
Для Mac OSX это не будет выполнено с ошибкой, solo(3801): Can't assign requested addressесли вы не введете 03-й параметр метода pack. То, что хорошо для BSD, хорошо и для Mac.
Эрик Лещинский
1

Вам нужен замок. run-oneделает работу, но вы также можете посмотреть flockиз util-linuxпакета.

Это стандартный пакет, предоставляемый разработчиками ядра, допускающий больше настроек, чем он, run-oneи все еще очень простой.

пенополистирол летать
источник
0

Простое решение от bash-hackers.org, которое работало для меня, было использование mkdir . Это простой способ убедиться, что запущен только один экземпляр вашей программы. Создайте каталог с помощью mkdir .lock, который возвращает

  • истина, если создание было успешным и
  • false, если файл блокировки существует, что указывает на то, что в данный момент запущен один экземпляр.

Итак, эта простая функция сделала всю логику блокировки файлов:

if mkdir .lock; then
    echo "Locking succeeded"
    eval startYourProgram.sh ;
else
    echo "Lock file exists. Program already running? Exit. "
    exit 1
fi


echo "Program finished, Removing lock."
rm -r .lock
domih
источник
0

Это решение для bash-скрипта, который должен проверить себя

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
ahofmann
источник