Uzun bir cron işinin paralel çalışmasını engelleyen genel çözüm?


27

Herhangi bir betiği veya uygulamayı crontab'da çalıştırmanıza ve iki kez çalışmasını engellemenize izin veren basit ve genel bir çözüm arıyorum.

Çözüm, yürütülen komutta bağımsız olmalıdır.

lock && (command ; unlock)Başka bir kilit olsaydı, kilidin nereye yanlış döneceği gibi görünmesi gerektiğini düşünüyorum .

İkinci bölüm, hata döndürse bile, komutu çalıştırdıktan sonra kilidi, run komutunu ve kilidini edinmiş gibi olur.

Yanıtlar:


33

Bir göz atın çalışma tekRun-one'ı yükle pakette. Gönderen için man run-onekomutuManpage simgesi :

run-one , bazı komutların benzersiz bir örneğini benzersiz bir argüman kümesiyle çalıştıran bir sarmalayıcı komut dosyasıdır.

Bir kerede birden fazla kopya çalıştırmak istemiyorsanız, bu genellikle cronjobs'ta kullanışlıdır.

Gibi timeveya sudo, sadece komutu için hazırlarsınız. Yani bir cronjob gibi görünebilir:

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

Daha fazla bilgi ve geçmiş için, Dustin Kirkland tarafından tanıtılan blog gönderisine bakın .


5

Bir kilit yerleştirmenin çok basit bir yolu:

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

Çalıştırmak isteyen bir betik için kilidi oluşturmak gerekir. Kilit varsa, başka bir komut dosyası meşgul, bu nedenle ilk komut dosyası çalıştırılamıyor. Dosya yoksa, hiçbir komut kilidi alamaz. Böylece mevcut komut dosyası kilidi alır. Betik bittiğinde, kilidi kaldırarak kilidi gerçekleştirmeniz gerekir.

Bash kilitleri hakkında daha fazla bilgi için bu sayfayı kontrol edin.


1
Ayrıca çıkıştaki kilidi kaldıran bir EXIT tuzağı da isteyeceksiniz. echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
geirha

1
İdeal olarak, danışmanlık sürüsünü, alt görev olarak istediğiniz komutu çalıştıran bir işlemden kullanmak isteyebilirsiniz. Bu şekilde hepsi ölürse sürü otomatik olarak serbest bırakılır, ki bu bir kilit dosyasının varlığını kullanmaz . Bir ağ bağlantı noktasının kullanılması benzer bir şekilde çalışacak - bu olsa yolu bir sorun daha küçük ad alanı.
Alex North-Keys

3

Fantezi bir paket kurmanıza gerek yok:

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

Bu betiği kendiniz yazmak "apt-get install" komutundan daha hızlıdır, değil mi? Yalnızca geçerli kullanıcı tarafından çalıştırılan örnekleri kontrol etmek için pgrep'e "-u $ (id -u)" eklemek isteyebilirsiniz.


2
bu tek bir örneği garanti etmez. iki komut dosyası, ||operatörün diğer tarafına aynı anda geçebilir ; komut dosyası henüz başlamadı.
Sedat Kapanoglu

@SedatKapanoglu Verilmiş, bu senaryo yarış koşulu koşulu değildir, ancak asıl soru uzun süredir çalışan cron-işleriyle ilgilidir (en çok dakikada bir kez çalıştırılır). Sisteminizin işlemin oluşturulması için bir dakikadan daha fazlasına ihtiyacı varsa, başka sorunlarınız da var. Ancak, başka nedenlerden dolayı gerekirse, yukarıdaki betiği yarış koşullarına karşı korumak için sürüyü (1) kullanabilirsiniz.
Michael Kowhan,

Bunu kullandım ama kendini kontrol etmesi gereken bir bash betiği için. Kod şudur: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && çıkış 2
ahofmann

3

Ayrıca solo, kullanıcıya özel bir geridöngü adresindeki bir bağlantı noktasını bağlayarak kilitleme gerçekleştiren Tim Kay'a bakın :

http://timkay.com/solo/

Site düşerse diye:

Kullanımı:

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

Bu şekilde kullanın:

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

Senaryo:

#!/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 için, solo(3801): Can't assign requested addressa 0yönteminin 3. parametresi için zorlamadığınız sürece Hata ile başarısız olur pack. BSD için iyi olan şey Mac için de iyidir.
Eric Leschinski

1

Bir kilide ihtiyacın var. run-oneişi yapar, ama aynı zamanda içine bakmak isteyebilirsiniz flockgelen util-linuxpaketin.

Çekirdek geliştiricileri tarafından sağlanan standart bir pakettir, daha çok kişiselleştirmeye izin verir run-oneve hala çok basittir.


0

Bash-hackers.org'dan benim için çalışan basit bir çözüm mkdir kullanıyordu . Bu, programınızın yalnızca bir örneğinin çalıştığından emin olmanın kolay bir yoludur. Bir dizin oluşturun mkdir .Lock hangi döner

  • oluşturma başarılı olursa doğru ve
  • Kilit dosyası mevcutsa false , şu anda çalışan bir örnek olduğunu gösterir.

Yani bu basit fonksiyon tüm dosya kilitleme mantığını yaptı:

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

Bu çözüm, kendisini kontrol etmesi gereken bir bash betiği içindir.

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.