KEY Talks #4 – PHP und Asynchrones I/O

Diesen Monat standen die Key-Talks im Zeichen der asynchronen Programmierung. Vorgestellt wurden unter anderem neue Features der ECMAScript Spezifikation für Javascript, mit denen man asynchrone Funktionsaufrufe mittels neuer Sprachkonstrukte umsetzen kann.

Asynchrone Aufrufe haben sich im Frontend Bereich durch Vorreiter wie AJAX schon längst durchgesetzt, doch wie sieht es im Backendbereich aus? Muss man auf Node.js oder Java ausweichen um Asynchronität zu erreichen? Oder gibt es Möglichkeiten auch PHP & Laravel mit diesen Techniken anzureichern? Und warum das Ganze? All das wollen wir in diesem Blogpost beleuchten. Eine Zusammenfassung des Vortrags diesen Monat mit dem Thema „Asynchrones PHP“.

Was ist Asynchronität ?

Traditionell laufen PHP Programme sequentiell ab, das heißt Befehle werden nacheinander ausgeführt, wobei bei jedem Befehl das Ergebnis abgewartet wird, bevor zum nächsten übergegangen wird. Für einen Webserver bedeutet das, dass für jeden Request ein PHP Skript aufgerufen und bis zum letzten Befehl abgearbeitet wird. Für den nächsten Request wird dann das Skript erneut von Vorne gestartet.

Das bedeutet auch, dass PHP nur einen Thread für die Abarbeitung des aktuellen Requests hat. Das heißt, wenn das Skript auf eine I/O Operation wartet, stoppt die Ausführung bis diese I/O Operation ein Ergebnis zurückgeliefert hat. Man spricht in diesem Fall von „Blockierenden“ I/O Operationen.

Javascript mit Node.js im Vergleich ist genauso Single-Threaded, mit dem Unterschied, dass es intern einen Event Loop implementiert. Dieser Event Loop checkt jede Iteration, ob ein Event gefeuert wurde und ruft entsprechende Callbacks auf, die mit diesem Event verknüpft sind.

Dadurch ist es möglich, asynchrone Funktionen zu definieren, indem man I/O Operationen Events triggern lässt, sobald ihre Arbeit abgeschlossen ist. Nun definiert man eine EventListener Funktion, welche sich um das Resultat der Operation kümmert, sobald dieses zu Verfügung steht. Danach kann man das Programm weiter laufen lassen. Wartezeiten gibt es hier nur, wenn das Ergebnis im weiteren Skriptverlauf benötigt wird, bevor die Operation abgeschlossen ist. Diese I/O Operationen werden als „nicht blockierend“ bezeichnet.

PHP-Native Implementierungen von Asynchronität

PHP selbst besitzt keinen Event-Loop, daher lässt sich das asynchrone Verhalten mit PHP nicht ohne Weiteres umsetzen. Es gibt jedoch eine Reihe von Optionen, um dieses Defizit auszugleichen.

Eine Möglichkeit bietet die pthreads Extension. Diese ermöglicht es PHP Threads zu erschaffen und parallel auszuführen. Dies ist jedoch ein äußerst umfangreiches Thema und soll nicht im Fokus dieses Beitrags liegen. Weiterhin eignet sich pthreads eher für CPU-lastige Aufgaben, während unser Fokus eher auf I/O lastigen Programmen liegt, welche viel mit Datenbanken, Web-APIs und dem Dateisystem arbeiten.

Eine weitere Möglichkeit ist, PHP mittels einer Bibliothek um Asynchronität zu erweitern. Mittlerweile sind einige dieser Bibliotheken für PHP in einem erwachsenen Zustand, sodass man sie ohne große Sorge für seine Projekte einsetzen kann.

Zu den bekanntesten zählen Icicle, ReactPHP, Amp und das noch recht junge kraken-php.

Die Unterschiede zwischen den Bibliotheken zu ergründen würde den Rahmen dieses Blogposts bei Weitem sprengen. Ähnlichkeiten sind jedoch nicht zu übersehen. Alle Bibliotheken beinhalten einen Eventloop wie bei Javascript, abstrahieren die Prozesse und Threaderzeugung und bieten verschiedene APIs um asynchrone Funktionen zu starten und deren Ergebnisse abzufragen. Manche versuchen die API so zu halten, dass sich der Programmierstil zwischen Asynchron und Synchron nicht zu sehr unterscheidet.

PHP Swoole

Allen vorangegangenen Bibliotheken ist der Aspekt gemeinsam, dass sie in reinem PHP verfasst wurden. Es gibt jedoch noch eine andere Variante: PHP Swoole

Swoole ist im Gegensatz zu den anderen Möglichkeiten in C geschrieben und als PHP PECL Extension verfügbar. Dadurch wird PHP durch Funktionen ergänzt, welche Asynchrones Programmieren ermöglichen. Im Vergleich zu seinen Konkurrenten schneidet Swoole bei der Performanz deutlich besser ab. Aber Swoole ist noch recht jung und es muss deshalb für Produktivumgebungen vorsichtig evaluiert werden, ob ein Einsatz sinnvoll ist und welche Alternativen existieren.

Im Vergleich zu den PHP-nativen Implementierungen eines EventLoop erreicht Swoole eine höhere Performanz, da es die Asynchronität auf Systemlevel implementiert. Da jedoch die Installation einer PECL Extension nötig ist, welche nicht auf jedem System zur Verfügung steht, ist es nicht so universell einsetzbar wie die nativen Alternativen.

HTTP-Server in PHP?

Neben den bereits zu Beginn genannten Möglichkeiten, I/O Operationen nicht blockierend und damit schneller auszuführen, bietet ein EventLoop auch die Möglichkeit, einen performanten HTTP Webserver direkt in PHP zu verfassen. So gut wie jede der vorher genannten Bibliotheken besitzt einen (mehr oder weniger simplen) Web Server um Requests asynchron zu bearbeiten.

Ein Vorteil von diesem Ansatz ist, dass keine gesonderten Prozesse für PHP mehr gestartet werden müssen. Ein großes Problem bei modernen PHP Frameworks ist deren zusätzlicher Overhead. Ein Framework braucht viel Zeit um die benötigten Klassen und Dateien zu laden und das bei jedem Request.

Kann ich mein Framework weiter benutzen?

Die Antwort lautet ganz klar: Jein. Frameworks nutzen normalerweise die PHP eigenen Funktionen um I/O Operationen durchzuführen und diese sind blockierend. Dennoch gibt es Möglichkeiten, die Vorteile von EventLoop und nicht blockierendem I/O mit dem persönlichen Lieblingsframeworks zu nutzen. Dazu müssen die I/O Abstraktionen des Frameworks in nicht blockierender Weise implementiert werden, was je nach Framework einiges an Aufwand bedeutet, falls es überhaupt vom Framework zugelassen wird. Ebenso gibt es weitere Stolperfallen zu beachten, wie z.B der Umgang mit Singletons. Sollte sich das Konzept Event-getriebenes, nicht blockierendes PHP jedoch durchsetzen, ist damit zu rechnen, dass Adaptionen für Frameworks in der Open Source Community recht schnell zur Verfügung stehen werden.

Für Laravel gibt es bereits ein Plugin um das Framework mittels eines Swoole-PHP Server laufen zu lassen, welches für jeden Request eine Sandbox Instanz von Laravel laufen lässt. Dadurch entsteht zwar wieder ein wenig mehr Overhead für die Erstellung der Sandbox, weit weniger jedoch als durch das erneute Laden des gesamten Frameworks enstehen würde.

Im Vergleich zu Apache oder Nginx lassen sich dadurch deutlich mehr Requests/sec bearbeiten.

Und die Zukunft?

Seit dem Launch der 7. Version bekommt PHP wieder Rückenwind im Backendbereich. Die Sprache entwickelt sich rasanter als noch vor ein paar Jahren. Neue oder bisher nicht unterstützte Programmierparadigmen werden rege diskutiert und in die Sprache mit aufgenommen. Möglicherweise wird asynchrone Programmierung ebenso in naher Zukunft fester Bestandteil der Sprache.

Nachteile?

Da dieses Paradigma allerdings einiges an Komplexität in PHP hinzufügt, ist es noch unklar, ob sich eine flächendeckende Unterstützung ohne weiteres in die Sprache integrieren lässt.

Denn alle Bibliotheken und Extensions die in diesem Beitrag genannt wurden, teilen ähnliche Nachteile. Viele Skripte die auf Globale Variablen setzen, bringen unvorhersehbaren Verhalten bei asynchronen Webservern mit sich. Ok, Globale Variablen gelten schon seit längerer Zeit als veraltet und sollten zugunsten Best Practices vermieden werden, doch finden sie sich dennoch häufig in Legacy Codebases und man ist gezwungen damit zu arbeiten. Auch andere Probleme, welche häufig durch den Einsatz von Threads enstehen, müssen beachtet werden, wie z.B korrektes Memory Management, vollständige Freigabe von Ressourcen, wenn Worker-Threads vorzeitig beendet werden (was im normalen PHP automatisch von der Garbage Collection vorgenommen wird), etc.

Einige dieser Probleme lassen sich sicherlich innerhalb der Bibliotheken vom Entwickler abstrahieren, sodass dieser sich allzu großen Sorgen machen muss. Andere wiederum werden neue Herausforderungen für PHP Entwickler darstellen.

Fazit

Während die Entwicklung vielversprechend scheint und in den nächsten Jahren bestimmt noch einiges bereit hält, sind die Technologien derzeit noch mit Vorsicht zu genießen. Neue Technologien beinhalten häufig neue Probleme, welche erst nach einiger Zeit ersichtlich werden. Dennoch gehen viele im Backend genutzte Sprachen einen ähnlichen Weg, weswegen es sich jetzt schon lohnt, mit der Materie vertraut zu werden.

Weiterhin fehlt es an einer ähnlich großen Community, aber auch hier ist in den letzten Jahren ein Anstieg zu beobachten, welcher in naher Zukunft vermutlich eher noch weiter zunehmen wird, als abzuflachen.

Die sehr vielversprechende Swoole Extension ist bisher hauptsächlich im asiatischen Raum verbreitet, vieles ist nur in Chinesisch dokumentiert oder besitzt gar keine Dokumentation, was die Nutzung für westliche Entwickler aktuell noch schwierig macht.

Am Ende des Vortrags halten wir fest, dass es wohl noch eine Weile dauern wird, bis sich asynchrones I/O in PHP durchsetzten wird, doch die Entwicklung der letzten Jahre hat den Weg dafür schon bereitet und es is wohl nur noch eine Frage der Zeit, bis es vollständig angekommen ist.