Asynchron Aufgaben erledigen (lassen)
Was könnte der Autor damit meinen, werdet ihr euch fragen. Naja, das ist schnell erklärt: hier und da hat man mal kleine Aufgaben, die evtl. etwas länger dauern, von dessen Ergebnis der User aber nicht wissen muss. Warum also soll man damit seine PHP-Scripte ausbremsen, und den User warten lassen? Ihn interessiert das Ergebnis ja (erstmal) nicht.
Beispiel: Nehmen wir an, das Versenden eine Informations-Email würde 1 Sekunde dauern (wenn man über einen externen SMTP versendet und nicht über den lokalen, kann das sogar sein). Dann bemerkt der User auf der Webseite eine Verzögerung, die Seite wird langsam.
Es gibt sicherlich noch bessere Beispiele, wie z.B. das Berechnen eines Vorschaubildes. Der User lädt ein Bild hoch (Avatar), und wir brauchen davon eine verkleinerte Version. Wenn dieser Algorithmus nun langsam wäre (was er im Normalfall nicht ist), würde der User vor seinem Bildschirm Däumchen drehen.
Für all solche Aufgaben (ihr findet sicherlich bessere) ist es sinnvoller, diese Aufgaben asynchron erledigen zu lassen. Da gibt es mindestens 2 Möglichkeiten:
- Man trägt die Aufgabe in eine Task-Tabelle in der Datenbank ein, und lässt ein weiteres Script (Cronjob?) diese Task-Liste abarbeiten.
- Man lässt einen Kindprozess diese Arbeit erledigen, welcher geforkt wird, und non-blocking ist
In beiden Fällen bekommt der User direkt die nächste Seite zu sehen und kann weiterarbeiten, der asynchrone Prozess kann aber erst einige Sekunden/Minuten später fertig sein.
Wie setzt man das nun um? Ich glaube, die erste Möglichkeit sollte jedem von euch bekannt sein, da es keine Schwierigkeit ist, eine neue Zeile in einer Datenbank-Tabelle einzufügen, und auch ein Script, das periodisch diese Zeilen liest und abarbeitet, sollte kein Hexenwerk sein.
Die zweite Möglichkeit haben wahrscheinlich deutlich weniger Leute genutzt. Ich finde sie auch ehrlich gesagt unschöner, aber möchte sie trotzdem vorstellen (da sie schneller umzusetzen ist, und häufig auch zum Ziel führt). Als erstes fällt einem da vielleicht der system() Aufruf ein, mit dem man einen weiteren Prozess starten kann. Das Problem daran ist, dass er blocking ist, sprich der nächste Befehl in PHP erst ausgeführt wird nachdem der System-Aufruf beendet ist. Genau das wollen wir ja nicht. Aber unter jedem Betriebssystem gibt es da Möglichkeiten, den Prozess in den Hintergrund zu schieben. Der system() Aufruf ist dann sofort beendet und PHP läuft weiter, parallel läuft ein weiterer Prozess mit der Aufgabe.
function execInBackground($cmd) { if (substr(php_uname(), 0, 7) == "Windows") { pclose(popen("start /B ". $cmd, "r")); } else { system($cmd . " > /dev/null &"); } }
In Maugrim The Reaper’s Blog gibt es eine sehr ausführliche Serie zu dem Thema, mit vielen weiteren Beispielen und Code. Auf jeden Fall lesenswert.
Eine andere interessante Möglichkeit ist Gearman.
http://toys.lerdorf.com/archives/51-Playing-with-Gearman.html
Wenn man etwas andere Voraussetzung hat, könnten die curl_multi_* Funktionen interessant sein. Diese kann man nutzen, wenn man viele Dinge zu erledigen hat, aber auf die Ergebnisse warten muß (weil man sie dem User anzeigen muss). Man möchte also nicht asynchron, sondern simultan arbeiten. Das ganze ist in einem Blogartikel zum Thema „Simultaneuos HTTP requests in PHP with cURL„ super beschrieben.
Man könnte doch sonst auch solche Aufgaben per AJAX im Hintergrund laufen lassen. Das stört den Benutzer ja auch nicht.
Aber sonst ist deine Idee, eine Art „Serverjob“ zu schreiben, echt interessant. Kann ich bestimmt irgendwann mal gebrauchen 😉
maTTes
31 Okt 09 at 15:16
Das geht nur solange der Besucher auch Javscript unterstützt. Leider kann man nicht davon ausgehen, dass das 100% sind, also fällt die Möglichkeit weg. Es muss schon rein serverseitig sein.
Michael Kliewe
31 Okt 09 at 17:12
[…] AW: Php-Script im Hintergrund laufen lassen Zu dem Thema habe ich hier einen guten Blogeintrag: https://www.phpgangsta.de/488 […]
Php-Script im Hintergrund laufen lassen - PHP @ tutorials.de: Forum, Tutorial, Anleitung, Schulung & Hilfe
17 Nov 09 at 00:02
[…] einem älteren Artikel schrieb ich bereits über asynchrone Aufgaben. Heute möchte ich Zend_Queue vorstellen, womit man Aufgaben speichern kann, die später erledigt […]
Taugt Zend_Queue etwas? | PHP Gangsta - Der PHP Blog
27 Nov 09 at 10:32
[…] einen blick rein werfen. da sind ein paar coole Sachen dabei um aus langsam schnell zu machen. das noch! ruby eventmachine dann noch ruby c extension und zu nodejs gibs auch ein chaosradio express […]
Sicherheit Socket-Server - Seite 2 - Flashforum
22 Feb 12 at 17:59
[…] sieht, muss ne Aktion ausgeführt werden, die länger dauert". Michael Kliewew hat im Artikel Asynchron Aufgaben erledigen lassen mal ein Beispiel dazu […]
Request nach dem Rendering abschicken - Zend Framework Forum - ZF1 / ZF2
25 Jun 12 at 10:16
[…] funktionieren nicht unter Windows und sind schwer zu bedienen. Man kann sich eventuell mit exec() behelfen und damit weitere Prozesse starten, verliert dann jedoch die Möglichkeit, die Prozesse zu […]
Richtige Threads in PHP einfach erstellen mit pthreads | PHP Gangsta - Der PHP Blog mit Praxisbezug
13 Mrz 13 at 12:28
Hallo Michael!
Habe noch Mal eine Frage zu der Multi-Task-Geschichte unter Windows 7. Dass mit popen() funktioniert prima. Ich kann beispielsweise 20 Prozesse starten und diese auch kontrolliert mittels MySQL beenden. Zu Testzwecken möchte ich die Prozessfenster sehen. Das klappt, indem ich einfach das /B weglasse. Nun würde ich den Fenstern gern Namen geben. Unter Commandline funktioniert das mit TITLE [Fenstername].
Hast du eine Idee, wie ich das umsetzen kann. Habe lange gesucht, aber leider nichts gefunden. Die Namen brauche ich, weil ich 6 verschiedene Programme gleichzeitig mit mehreren Tasks ausführen möchte. In den vielen Fenstern steht immer nur folgendes drin: c:/xampp/php/php.exe
Beste Grüße
Thor
Thor
2 Aug 13 at 00:18
@Thor Keine Ahnung ehrlich gesagt. Ab PHP 5.5 ist die Funktion cli_set_process_title() mit enthalten (vorher glaube ich via PECL verfügbar), aber keine Ahnung ob das unter Windows den Command-Line-Fenster-Titel ändert…
http://php.net/manual/en/function.cli-set-process-title.php
Michael Kliewe
2 Aug 13 at 00:26