Asynchrone PHP Prozesse mit Gearman

Gearman Es kommt immer wieder vor, dass man aus einem PHP-Script heraus ein Anderes aufrufen möchte welches dann eine bestimmt Aufgabe erledigt. Dies ist z.B. sinnvoll wenn die Aufgabe länger dauert und man nicht im Script darauf warten möchte, wie etwa beim Erstellen von Bildern in verschiedenen Größen. Ich hatte bereits vor einiger Zeit über eine Möglichkeit berichtet PHP-Script im Hintergrund auszuführen. Diese Möglichkeit ist allerdings nicht wirklich schön und hat auch einige Nachteile. Z.B. kann man nur schwer abfragen wie der aktuelle Status des Scripts ist und auch die Verteilung auf verschiedene Server ist nicht trivial. Darum möchte ich hier eine wesentlich elegantere Möglichkeit vorstellen PHP-Prozesse asynchron laufen zu lassen. Und zwar mit Hilfe von Gearman:

Gearman ist ein Jobserver und ermöglicht es Aufgaben auf verschiedene Prozesse und/oder Maschinen auszulagern. Dabei arbeitet man mit sogenannten "Workern" die auf einem beliebigen Server darauf warten Aufgaben zu erledigen. Besonders schön ist, dass diese Worker ebenfalls in PHP programmiert werden können. Zunächst jedoch ein paar Worte zur Installation.

Auf einem Debian Squeeze System ist die Installation denkbar einfach. Einfach den Jobserver via Aptitude oder apt-get installieren. Das Paket heisst "gearman". Im init.d Ordner liegt dann auch direkt ein Script mit dem der Server gestartet werden kann:

/etc/init.d/gearman-job-server start

Um den Gearman Server in PHP "ansprechen" zu können benötigt man nun noch die Gearman-Extension welche über PECL installiert werden kann. Hinweis: Es wird das Paket "php5-dev" benötigt. Falls dieses noch nicht installiert ist sollte man das an dieser Stelle tun.

Dann die PHP-Extenstion über PECL installieren:

pecl install gearman

Hinweis: Falls das Paket hier nicht installiert werden kann weil es eine Beta Version ist, stattdessen folgenden Befehl verwenden:

pecl install gearman-beta

Damit sollte die Extension erstellt sein und kann in der php.ini geladen werden. Dazu einfache folgendes in der php.ini ergänzen:

extension = gearman.so

Hinweis: Da die Worker normalerweise als CLI-Programme laufen, die entsprechende php.ini nicht vergessen.

Nun sollte alles soweit sein, dass Gearman verwendet werden kann.

Zunächst brauchen wir einen Worker welcher im Hintergrund läuft und darauf wartet Aufgaben zu erledigen. Hier ein Beispiel:

<?php
class WorkerExample
{
    private $_GearmanWorker = null;

    public function  __construct()
    {
        $this->_GearmanWorker = new GearmanWorker;
        $this->_GearmanWorker->addServer('127.0.0.1', 4730);

        $this->_startupWorker();
    }

    private function _startupWorker()
    {
        $this->_GearmanWorker->addFunction('foobar', array($this, 'foobar'));
        while($this->_GearmanWorker->work());
    }

    public function foobar($Job)
    {
        $params = array();
        $params = unserialize($Job->workload());

        // do some work here...
        var_dump($params);
    }
}

$Worker = new WorkerExample;

Diesen Worker kann man nun z.B. als worker.php speichern und dann zum testen in einem Screen laufen lassen:

php worker.php

Nun brauchen wir noch ein Script welches diesen Worker aufruft, der sogenannte Client:

<?php
$Client = new GearmanClient();
$Client->addServer('127.0.0.1', 4730);

$data = array(
    'foo' => 42,
    'bar' => 1337
);
$data = serialize($data);
$Client->doBackground('foobar', $data);

Wenn nun dieser Client aufgerufen wird sollte man in dem Screen in dem der Worker läuft eine Ausgabe der übergebenen Parameter sehen. Im Code sieht man sofort dass die IP des Servers auf dem Gearman läuft angegeben wird. Hier kann natürlich auch ein anderer Server als localhost benutzt werden. Mit "doBackground" wird der Worker aufgerufen und das Script läuft direkt weiter ohne auf irgendeine Antwort des Workers zu warten.

Über die vielen weiteren Möglichkeiten des Gearman-Servers kann man sich am besten informieren wenn man einen Blick auf die Methoden der Gearman-Extension auf php.net wirft. Und nun viel Spass beim experimentieren. ;)