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

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

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

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

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

33
задан 25 May 2012 в 13:03

7 ответов

Взгляните на пакет run-one . Из справочной страницы для команды run-one Manpage icon:

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

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

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

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

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

38
ответ дан 25 May 2012 в 13:03

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

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
0
ответ дан 25 May 2012 в 13:03

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

  • true, если создание прошло успешно, и
  • 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
    
0
ответ дан 25 May 2012 в 13:03

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

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

1
ответ дан 25 May 2012 в 13:03

См. Также 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;
3
ответ дан 25 May 2012 в 13:03

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

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

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

3
ответ дан 25 May 2012 в 13:03

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

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

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

Для получения дополнительных сведений о блокировках bash посетите эту страницу

5
ответ дан 25 May 2012 в 13:03

Другие вопросы по тегам:

Похожие вопросы: