Çalışan yinelenen cron işlerini önleme


92

Her dakika çalıştırmak için bir cron işi planladım ama bazen senaryo bitirmek bir dakikadan fazla sürüyor ve işlerin birbiri üzerine "istiflenmeye" başlamasını istemiyorum. Sanırım bu bir eşzamanlılık problemidir - yani senaryo yürütmesinin karşılıklı olarak dışlanması gerekir.

Sorunu çözmek için betiği belirli bir dosyanın varlığına (" lockfile.txt ") arattım ve varsa ya touchda yoksa çıktı. Ama bu oldukça berbat bir semafor! Bilmem gereken en iyi uygulama var mı? Bunun yerine bir daemon yazmalı mıyım?

Yanıtlar:


118

Bu özelliği otomatikleştiren, sıkıntıdan ve olası böceklerden kendiniz yapmaktan kaçınmak ve sahnelerin arkasındaki sürüyü kullanarak eski kilitleme sorununu önlemek için birkaç program vardır (bu yalnızca dokunma kullanıyorsanız bir risktir) . Ben lockrunve lckdogeçmişte kullandım, ama şimdi flock(1) (newish util-linux sürümlerinde) bu harika. Kullanımı gerçekten çok kolay:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

2
lckdo, daha fazla topuzdan kaldırılacak, şimdi sürü (1) util-linux'da. Bu paket Linux sistemlerinde temel olarak zorunludur, bu yüzden onun varlığına güvenebilmelisin. Kullanım için aşağıya bakınız.
jldugger

Evet, sürü şimdi tercih ettiğim seçenek. Cevabımı bile uyacak şekilde güncelleyeceğim.
womble

flock -n file commandVe arasındaki farkı bilen var mı flock -n file -c command?
Nanne

2
@Hayır, emin olmak için kodu kontrol etmek zorunda kalacağım, ancak benim eğitimli tahminim, -cbelirtilen komutu bir kabuk (manpage'e göre) çalıştırırken, "çıplak" (olmayan -c) form execverilen komuttur. . Kabuğa bir şey koymak, kabuk benzeri şeyler yapmanıza izin verir (örneğin ;veya ile ayrılmış birden fazla komut çalıştırmak gibi &&), ancak eğer güvenilmeyen girdi kullanıyorsanız, kabuk genişletme saldırılarına da açılır.
Womble

1
frequent_cron_jobHer dakika çalıştırıldığını göstermeye çalışan (varsayımsal) komutun bir argümanıydı . Yararlı bir şey eklemediğinden ve kafa karışıklığına neden olduğundan (yıllarca başka kimse olmazsa sizinki) çıkardım.
womble

28

Kabukta en iyi yol sürü (1) kullanmaktır

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

1
Fd yeniden yönlendirmenin zor kullanımını kullanamıyorum. Sadece çok arcanely harika.
womble

1
Bash veya ZSH benim için ayrıştırmak yapmaz, arasında boşluk ortadan kaldırmak gerekir 99ve >öyledir99> /...
Kyle Brandt

2
@Javier: Bu zor ve arcane değil, sadece belgelenmiş , zor ve arcane anlamına gelmez .
womble

1
bu çalışırken yeniden başlatırsanız veya işlemi bir şekilde öldürürseniz ne olur? Sonsuza dek kilitli kalır mıydı?
Alex R,

5
Bu yapının özel bir kilit yarattığını anlıyorum ama bunun nasıl yapıldığının mekanizmasını anlamıyorum. Bu cevabın içindeki '99' işlevi nedir? Bunu açıklamak isteyen var mı? Teşekkürler!
Asciiom

22

Aslında * flock -nyerine kullanılabilir lckdo, bu nedenle çekirdek geliştiricilerin kodunu kullanacaksınız.

Womble'ın örneğini temel alarak , şöyle bir şey yazardınız :

* * * * * flock -n /some/lockfile command_to_run_every_minute

BTW, tüm kod bakarak flock, lockrunve lckdoaynı şeyi yapmak, bu yüzden en hazır size olduğu meselesi.


2

Bir kilit dosyası kullanabilirsiniz. Betik başladığında bu dosyayı oluşturun ve bittiğinde silin. Komut, ana rutini çalıştırmadan önce, kilit dosyasının var olup olmadığını kontrol etmeli ve buna göre devam etmelidir.

Kilit dosyaları initscripts ve Unix sistemlerinde birçok diğer uygulama ve yardımcı program tarafından kullanılır.


1
bu sadece şimdiye kadar bizzat uygulamaya gördüğüm şekilde. Bir ÖSS projesi için bir ayna olarak güncelleyicisinin öneri gereğince kullanmak
warren

2

Komut dosyasının önceki çalışmanın tamamlanmasını beklemesini beklemesini istiyorsanız bunu belirtmediniz. "İşlerin" üst üste "üst üste" başlamasını istemiyorum "ifadesiyle, bence zaten çalışıyorsa betiğin çıkmasını istediğinizi ima ediyorsunuz.

Öyleyse, lckdo ya da benzerlerine güvenmek istemiyorsanız, şunu yapabilirsiniz:


PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work


Teşekkürler, örneğiniz faydalı: Eğer çalışıyorsa betiğin çıkmasını istiyorum. Ickdo'dan bahsettiğiniz için teşekkürler - hile yapıyor gibi görünüyor.
Tom

FWIW: Bu çözümü sevdim çünkü bir betiğe dahil edilebiliyor, bu nedenle kilitleme betiğin nasıl çağrıldığına bakılmaksızın çalışıyor.
David G

1

Bu aynı zamanda yanlış bir şey yaptığınıza dair bir işaret olabilir. Eğer işleriniz bu kadar yakın ve sık sık çalışıyorsa, belki onu çözmeyi ve bir daemon tarzı program yapmayı düşünmelisiniz.


3
Ben yürekten buna katılmıyorum. Periyodik olarak çalışması gereken bir şeye sahipseniz, onu cini yapmak bir "fındık için balyoz" çözümüdür. Kazaları önlemek için bir kilit dosyası kullanmak, kullanmakta hiç sorun yaşamadığım mükemmel bir çözüm.
womble

@ womble Katılıyorum; ama balyozlarla fındık kırmayı seviyorum! :-)
wzzrd

1

Daha önceki örnekleri hala çalışıyorsa, cron daemon'unuzun işleri başlatmaması gerekir. Ben bir cron daemon dcron'un geliştiricisiyim ve özellikle bunu önlemeye çalışıyoruz. Vixie cronu veya diğer zindanların bununla nasıl başa çıktığını bilmiyorum.


1

Run-one komutunu kullanmanızı tavsiye ederim - kilitlerle uğraşmaktan çok daha basit. Dokümanlardan:

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.

run-this-one tam olarak run-one gibidir, ancak kullanıcının sahip olduğu tüm koşu işlemlerini bulup öldürmek ve hedef komutları ve argümanları eşleştirmek için pgrep ve kill komutunu kullanır. Run-this-one'ın, eşleşen tüm işlemleri bitene kadar eşleşen işlemleri öldürmeye çalışırken engelleneceğini unutmayın.

run-one-sürekli , "COMMAND [ARGS]" komutunu herhangi bir zamanda COMMAND çıktıktan sonra (sıfır veya sıfır olmayan) yeniden düzenleme dışında, tam olarak run-one gibi çalışır.

bir çalışan -sürekli çalışan bir takma addır.

başarıya kadar koşmak, tam olarak çalıştırmak için tam olarak koşmak gibi çalışır; ancak, COMMAND başarıyla çıkana kadar (örneğin, sıfırdan çıkıncaya kadar) "COMMAND [ARGS]" komutunu yeniden düzenler.

run-one- up-fail, COMMAND başarısızlıkla çıkana kadar (yani sıfır olmayan bir şekilde) çıkıncaya kadar, "COMMAND [ARGS]" ifadesini yeniden eşleştirmesi dışında sürekli olarak bir-run-one gibi çalışır.


1

Artık bu sistem dışarı çıktı, Linux sistemlerinde başka bir zamanlama mekanizması daha var:

bir systemd.timer

İçinde /etc/systemd/system/myjob.serviceveya ~/.config/systemd/user/myjob.service:

[Service]
ExecStart=/usr/local/bin/myjob

İçinde /etc/systemd/system/myjob.timerveya ~/.config/systemd/user/myjob.timer:

[Timer]
OnCalendar=minutely

[Install]
WantedBy=timers.target

Zamanlayıcı sonraki aktive olduğunda hizmet birimi zaten aktive edilirse, hizmetin daha sonra başka örneği olacak değil başlanacaktır.

İşi önyüklemede bir kez ve her çalıştırmadan bir dakika sonra başlatan bir alternatif:

[Timer]
OnBootSec=1m
OnUnitInactiveSec=1m 

[Install]
WantedBy=timers.target

0

Yinelenen crons gibi çalışan bu sorunu çözmek için bir kavanoz oluşturduk java veya kabuk cron olabilir. Sadece Duplicates.CloseSessions ("Demo.jar") içine cron adını yazın. Bu işleri yapmak için bir yöntem kullandım. Dize proname = ManagementFactory.getRuntimeMXBean (). GetName (); String pid = proname.split ("@") [0]; System.out.println ("Geçerli PID:" + pid);

            Process proc = Runtime.getRuntime().exec(new String[]{"bash","-c"," ps aux | grep "+cronname+" | awk '{print $2}' "});

            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String s = null;
            String killid="";

            while ((s = stdInput.readLine()) != null ) {                                        
                if(s.equals(pid)==false)
                {
                    killid=killid+s+" ";    
                }
            }

Ve sonra yine kabuk komutuyla killid ipini öldür


Bunun gerçekten soruyu cevapladığını sanmıyorum.
kasperd

0

@Philip Reynolds cevabı, 5 saniyelik bekleme süresinin sonunda kilitlenmeden kodu çalıştırmaya başlayacaktır. Aşağıdaki Flock çalışıyor gibi görünmüyor ben @Philip Reynolds cevabı modifiye

(
  flock -w 5 -x 99 || exit 1
  ## Do your stuff here
) 99>/path/to/my.lock

Böylece kod hiçbir zaman eşzamanlı çalıştırılmaz. Bunun yerine 5 saniye bekledikten sonra işlem o zamana kadar kilidi alamazsa 1 ile çıkacaktır.

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.