PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Archive for the ‘Gearman’ tag

Asynchron Aufgaben erledigen (lassen)

with 9 comments

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.

Written by Michael Kliewe

Oktober 30th, 2009 at 7:49 pm

Posted in PHP

Tagged with , , , , ,