Wie man cron mehr als einmal pro Minute mit PHP ausfĂĽhrt

Die klassische Konfigurationsdatei für Cron-Job-Einträge im Linux-Betriebssystem lautet wie folgt:

# ┌─────────────  (0 - 59)
# │ ┌─────────────  (0 - 23)
# │ │ ┌─────────────  (1 - 31)
# │ │ │ ┌─────────────  (1 - 12)
# │ │ │ │ ┌─────────────   (0 - 6)
# │ │ │ │ │
# * * * * * <  >

Die ersten fĂĽnf Parameter geben die AusfĂĽhrungszeit dieser Aufgabe an, und der sechste - der Befehl selbst, der ausgefĂĽhrt werden muss. Zeitparameter sind: Minuten, Stunden, Tage, Monate und Wochentag. DarĂĽber hinaus mĂĽssen alle Zahlen als Ganzzahlen oder in Form einer speziellen Syntax dargestellt werden .

Infolgedessen beträgt das minimal mögliche Intervall zum Starten eines Befehls einmal pro Minute.

Für viele Aufgaben wird die Befehlsausführung viel häufiger benötigt, beispielsweise alle 10 Sekunden. Bei einigen Aufgaben zur Automatisierung von Geschäftsprozessen beträgt die maximal zulässige Verzögerung häufig nicht mehr als 1 bis 1,5 Sekunden.

NatĂĽrlich ist der klassische Cron dafĂĽr nicht geeignet - er muss verbessert werden.

Im Folgenden finden Sie eine schrittweise Implementierung zum Erstellen zusätzlicher Funktionen (in PHP) für das klassische Cron unter Linux unter Verwendung eines zusätzlichen Schutzes gegen Neustartprozesse.

Aufgabeneinstellung und Cron-Einrichtung


Zum Beispiel werden wir die folgende Aufgabe verwenden:

  1. Im Frontend kann der Benutzer die Ausführung einer komplexen Aufgabe durch Klicken auf die Schaltfläche "Ausführen" einleiten.
  2. Das Backend nach dem Schreiben einer neuen Zeile in die Datenbank informiert den Benutzer über die Bestätigung.
  3. Über cron werden wir solche neuen Aufgaben „verfolgen“ und so schnell wie möglich erledigen, damit der Benutzer das Ergebnis nicht in einer Minute, sondern sofort * erhält.

* Wenn Sie den Start von Befehlen nur pro Minute verwenden, wird die Aufgabe gestartet, wenn die Sekunden die nächste Null erreichen (den Beginn einer neuen Minute). Daher muss der Benutzer in der klassischen Form von 0 bis 59 Sekunden auf die Ausführung warten.

So wird cron in seiner maximalen Form konfiguriert, d.h. einmal pro Minute:

* * * * * /usr/bin/php -q /run.php > /dev/null 2>&1
#/usr/bin/php -    PHP   (      )
#/run.php -   PHP   
#> /dev/null 2>&1 - ,            run.php

Einzelzyklus


Zunächst sollten Sie entscheiden, wie oft neue Aufgaben in der Datenbank angefordert werden sollen. Abhängig davon ändern sich die Anzahl der Zyklen und der logische Ruhezustand (Funktion sleep) .

Im aktuellen Beispiel wird ein Schritt von 10 Sekunden verwendet. Daher beträgt die Anzahl der Zyklen 60/10 = 6. Insgesamt lautet der allgemeine Code wie folgt:

for ($cycle = 1; $cycle <= 6; $cycle++) { # 6 
    $all_tasks = get_all_tasks(); #     
    if ($all_tasks) { #    - 
        foreach($all_tasks as $one_task) { #     
            solve_one_task($one_task); #  
        }
    }
    sleep(10); #  ,  ""  10 
}

Erläuterung : In diesem Beispiel wird ein Schritt von 10 Sekunden verwendet, der alle 10 Sekunden ein Mindestintervall für die Skriptausführung bereitstellen kann. Dementsprechend sollten Sie für eine häufigere Ausführung die Anzahl der Zyklen und die "Schlafzeit" ändern.

Vermeiden der wiederholten AusfĂĽhrung von Aufgaben


In der dargestellten Form gibt es eine Unsicherheit, nämlich die wiederholte Ausführung der Aufgabe, wenn sie bereits gestartet wurde. Dies gilt insbesondere dann, wenn die Aufgabe "schwierig" ist und einige Sekunden benötigt, um sie zu implementieren.

Dies fĂĽhrt zu einem erneuten AusfĂĽhrungsproblem:

  • Die Funktion wird solve_one_task()bereits ausgefĂĽhrt, hat ihre Arbeit jedoch noch nicht abgeschlossen.
  • Daher wird die Aufgabe in der Datenbank immer noch als nicht erfĂĽllt markiert.
  • Im nächsten Zyklus wird diese Aufgabe erneut ausgefĂĽhrt und die Funktion solve_one_task()mit derselben Aufgabe erneut ausgefĂĽhrt.

Dies kann natürlich gelöst werden, indem beispielsweise ein Status in der Datenbank für diese Aufgabe geändert wird.

Wir werden die Datenbank jedoch nicht laden: Aufgrund meiner Tests kann MYSQL die Anforderung akzeptieren, aber nicht sofort verarbeiten. Eine Differenz von sogar 0,5 Sekunden kann zu einer wiederholten AusfĂĽhrung fĂĽhren - was kategorisch nicht geeignet ist.

Auch in diesem Fall sprechen wir nur ĂĽber den Status von Aufgaben, daher ist es besser, das Dateisystem des Servers zu verwenden.

Das grundlegende ĂśberprĂĽfungsmodell wird mit flockeiner Funktion erstellt, mit der eine Datei festgelegt und entsperrt wird.

In der PHP-AusfĂĽhrung kann die Funktion wie folgt dargestellt werden:

$lock_file_abs = 'file'; #  
$fp = fopen($lock_file_abs,"w+"); #   
if (flock($fp, LOCK_EX | LOCK_NB)) { #,    
    solve_one_task($one_task); #  
    flock($fp, LOCK_UN); #   ,     
}
else {
    #,   , ..       
}
fclose($fp); #   
unlink($lock_file_abs); # ,   

Ergebnis


Die allgemeine Ansicht des gesamten Zyklus ist wie folgt:

for ($cycle = 1; $cycle <= 6; $cycle++) {
    $all_tasks = get_all_tasks();
    if ($all_tasks) {
        foreach($all_tasks as $one_task) {
            $lock_file_abs = __DIR__.'/locks/run_'.$one_task['id'];
            $fp = fopen($lock_file_abs,"w+");
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                solve_one_task($one_task);
                flock($fp, LOCK_UN);
            }
            else {
                #    
            }
            fclose($fp);
            unlink($lock_file_abs);
        }
    }
    sleep(10);
}

Ein solcher Algorithmus ermöglicht es Ihnen daher, eine zyklische Ausführung der Aufgabenverarbeitung zu starten, und befürchtet nicht, dass die Aufgabe mehr als einmal verarbeitet wird.

Und jetzt reicht es aus, diesen Code einfach jede Minute durch cron zu fĂĽhren, und es werden wiederum kleinere Zyklen ausgefĂĽhrt, die bereits in sich selbst enthalten sind.

All Articles