Vraag Voorkom dat dubbele cron-taken worden uitgevoerd


Ik heb een cron-taak gepland die elke minuut wordt uitgevoerd, maar soms duurt het script meer dan een minuut om te voltooien en ik wil niet dat de taken boven elkaar beginnen te "stapelen". Ik veronderstel dat dit een gelijktijdigheidsprobleem is - dat wil zeggen dat de uitvoering van het script elkaar wederzijds moet uitsluiten.

Om het probleem op te lossen, heb ik het script laten zoeken naar het bestaan ​​van een bepaald bestand ("lockfile.txt") en verlaat het als het bestaat of touch het als het dat niet doet. Maar dit is een mooie belabberde semafoor! Bestaat er een goede werkwijze die ik moet kennen? Moet ik in plaats daarvan een daemon hebben geschreven?


81
2017-11-09 11:32


oorsprong




antwoorden:


Er zijn een aantal programma's die deze functie automatiseren, de ergernis en mogelijke bugs wegnemen om dit zelf te doen, en het stallock-probleem vermijden door ook flock achter de schermen te gebruiken (wat een risico is als je alleen touch gebruikt) . Ik heb gebruikt lockrun en lckdo in het verleden, maar nu is er flock(1) (in nieuwe versies van util-linux) wat geweldig is. Het is echt gemakkelijk te gebruiken:

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

109
2017-11-09 11:57



lckdo wordt verwijderd uit meerutils, nu dat flock (1) in util-linux is. En dat pakket is in principe verplicht in Linux-systemen, dus je moet op zijn aanwezigheid kunnen vertrouwen. Zie hieronder voor gebruik. - jldugger
Ja, flock is nu mijn voorkeursoptie. Ik zal zelfs mijn antwoord aanpassen. - womble♦
Weet iemand het verschil tussen flock -n file command en flock -n file -c command ? - Nanne
@Nanne, ik zou de code moeten controleren om zeker te zijn, maar mijn opgeleide gok is dat -c voert de opgegeven opdracht uit via een shell (volgens de manpage), terwijl de "bare" (niet-actieve)-c) vorm gewoon execs het gegeven commando. Iets door de shell laten kan je shell-achtige dingen doen (zoals het uitvoeren van meerdere commando's gescheiden met ; of &&), maar opent ook u voor shell-uitbreidingsaanvallen als u onbetrouwbare invoer gebruikt. - womble♦
Het was een argument voor de (hypothetische) frequent_cron_job commando dat probeerde te laten zien dat het elke minuut werd afgespeeld. Ik heb het verwijderd omdat het niets nuttigs toevoeg, en verwarring veroorzaakte (het jouwe, als niemand anders het door de jaren heen heeft gedaan). - womble♦


De beste manier om te gebruiken is om te gebruiken flock (1)

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

27
2017-11-09 11:45



Ik kan een lastig gebruik van fd-omleiding niet overslaan. Het is gewoon te geweldig geweldig. - womble♦
Laadt mij niet in Bash of ZSH, moet de ruimte ertussen verwijderen 99 en > zo is het 99> /... - Kyle Brandt♦
@Javier: betekent niet dat het niet lastig en geheimzinnig is, alleen dat het dat is gedocumenteerd, lastig en geheimzinnig. - womble♦
wat zou er gebeuren als je herstart terwijl dit wordt uitgevoerd of het proces op de een of andere manier wordt gedood? Zou het dan voor altijd gesloten zijn? - Alex R
Ik begrijp dat deze structuur een exclusief slot creëert, maar ik begrijp de mechaniek niet van hoe dit wordt bereikt. Wat is de functie van de '99' in dit antwoord? Kan iemand dit uitleggen alstublieft? Bedankt! - Asciiom


Werkelijk, flock -n kan worden gebruikt in plaats van lckdo*, dus je zult code van kernelontwikkelaars gebruiken.

Voortbouwen op het voorbeeld van womble, zou je iets schrijven als:

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

Trouwens, kijkend naar de code, allemaal flock, lockrun, en lckdo precies hetzelfde doen, dus het is gewoon een kwestie van wat het gemakkelijkst voor u beschikbaar is.

* Omdat ik, gezien mijn reputatie op het moment van schrijven, eerdere antwoorden niet kan bewerken of becommentariëren, moet ik dit als een apart antwoord schrijven.


21
2017-11-19 22:43





U kunt een vergrendelingsbestand gebruiken. Maak dit bestand wanneer het script start en verwijder het als het klaar is. Het script moet, voordat het zijn hoofdroutine uitvoert, controleren of het vergrendelingsbestand bestaat en dienovereenkomstig verder gaan.

Lockfiles worden gebruikt door initscripts en door vele andere toepassingen en hulpprogramma's in Unix-systemen.


2
2017-11-09 11:36



dit is de enkel en alleen hoe ik het ooit persoonlijk heb geïmplementeerd. Ik gebruik op basis van de suggestie van de beheerder als een spiegel voor een OSS-project - warren


Dit kan ook een teken zijn dat je het verkeerde doet. Als je taken zo goed en zo vaak lopen, moet je misschien overwegen om het te de-coderen en het een programma in de vorm van een daemon te maken.


1
2017-11-09 11:45



Ik ben het hier van harte mee oneens. Als je iets hebt dat periodiek moet worden uitgevoerd, is een daemon een "moker voor een moer" -oplossing. Het gebruik van een vergrendelingsbestand om ongelukken te voorkomen is een volkomen redelijke oplossing waar ik nog nooit een probleem mee heb gehad. - womble♦
@womble ik ga akkoord; maar ik hou van noten slaan met voorhamers! :-) - wzzrd


U heeft niet opgegeven of u wilt dat het script wacht tot de vorige uitvoering is voltooid of niet. Met "Ik wil niet dat de taken beginnen te" stapelen over elkaar heen ", denk ik dat u aangeeft dat u wilt dat het script wordt afgesloten als het al actief is,

Dus als u niet afhankelijk wilt zijn van lckdo of vergelijkbaar, kunt u dit doen:


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


1
2017-11-09 14:33



Bedankt dat uw voorbeeld nuttig is - ik wil wel dat het script wordt afgesloten als het al actief is. Bedankt voor het vermelden ickdo - Het lijkt de truc te doen. - Tom


Uw cron-daemon mag geen taken oproepen als eerdere exemplaren nog steeds worden uitgevoerd. Ik ben de ontwikkelaar van één cron-daemon dcronen we proberen dit specifiek te voorkomen. Ik weet niet hoe Vixie cron of andere daemons hiermee omgaan.


1
2018-02-17 15:59





Ik zou aanraden om te gebruiken run-one commando - veel eenvoudiger dan het omgaan met de sloten. Van de documenten:

run-one is een wrapscript dat niet meer dan één unieke instantie van een of andere opdracht met een unieke set argumenten uitvoert. Dit is vaak handig met cronjobs, wanneer u niet meer dan één kopie tegelijk wilt gebruiken.

run-this-one is precies hetzelfde als een run-one, behalve dat het gebruikmaakt van pgrep en kill om alle lopende processen van de gebruiker te vinden en te doden en het matchen van de doelopdrachten en -argumenten. Houd er rekening mee dat run-this-one geblokkeerd wordt tijdens het proberen om matchende processen te doden, totdat alle overeenkomsten zijn gevonden processen zijn dood.

run-een-constant werkt precies zoals run-one, behalve dat het respondeert "COMMAND [ARGS]" op elk moment dat COMMAND verlaat (nul of niet nul).

keep-één-running is een alias voor run-one-constant.

run-één-tot-succes werkt precies zoals run-one-constant behalve dat "COMMAND [ARGS]" opnieuw wordt weergegeven totdat COMMAND met succes wordt afgesloten (bijv. verlaat nul).

run-één-tot-storing werkt precies zoals run-one-constant behalve dat "COMMAND [ARGS]" opnieuw wordt weergegeven totdat COMMAND wordt afgesloten met een fout (bijv. sluit niet-nul af).


1
2017-10-02 21:53





Ik heb een pot gemaakt om een ​​dergelijk probleem op te lossen, zoals dubbele crons die kunnen worden uitgevoerd, java of shell cron. Geef de cron-naam net door in Duplicates.CloseSessions ("Demo.jar") dit zal searchng pid zoeken en doden voor deze cron, behalve de huidige. Ik heb een methode geïmplementeerd om dit te doen. String proname = ManagementFactory.getRuntimeMXBean (). GetName ();                 String pid = voornaam.split ("@") [0];                 System.out.println ("Huidige 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+" ";    
                }
            }

En killid-string doden met opnieuw shell-commando


0
2017-12-28 08:36



Ik denk niet dat dit echt de vraag beantwoordt. - kasperd