Seite wählen

NETWAYS Blog

Event-driven, async I/O with PHP

Ever wondered whether it is possible to write asynchronous code in PHP? Good news, it is! One of the most promising libraries is ReactPHP. It provides the powerful concept of event-driven, non-blocking I/O in PHP. Its core is an event loop, on top of which it provides utilities like stream abstraction and async network clients and servers. The library is written in pure PHP and its architecture is well-suited for high performance network servers and clients. The documentation and examples of ReactPHP’s components are quite detailed so it should be no problem to get started. The following example is a simple asynchronous web server written in ReactPHP which responds with „Hello World“ for every request.

$loop = React\EventLoop\Factory::create();
$server = new React\Http\Server(function (Psr\Http\Message\ServerRequestInterface $request) {
    return new React\Http\Response(
        200,
        array('Content-Type' => 'text/plain'),
        "Hello World!\n"
    );
});
$socket = new React\Socket\Server(8080, $loop);
$server->listen($socket);
echo "Server running at http://127.0.0.1:8080\n";
$loop->run();
Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.

Asynchroner Webserver in Python mit gevent

Was ist gevent?
gevent ist eine Koroutinen-basierte Netzwerkbibliothek für Python basierend auf libev und greenlet.
greenlets sind leichtgewichtige Koroutinen, die im Prozess des ausführenden Programms laufen, aber nebenläufig ausgeführt werden. Im Gegensatz dazu werden beim Multitasking, Threads vom Betriebssystem geplant, die echt gleichzeitig laufen.
Parallelität vs Nebenläufigkeit
Zwei Aufgaben heißen nebenläufig, wenn sie voneinander unabhängig, in einem sich überschneidenden Zeitfenster, abgearbeitet werden. Dabei müssen diese nicht unbedingt echt gleichzeitig bearbeitet werden.
Zwei Aufgaben werden parallel bearbeitet, wenn sie unabhängig voneinander und zur gleichen Zeit ausgeführt werden.
„Benchmark“
Um die Performance von gevent zu vergleichen (Achtung nicht repräsentativ), lassen wir es gegen Twisted und wsgiref antreten. Die drei Kandidaten starten jeweils einen WSGI Server auf http://127.0.0.1:8088/ und beantworten jede Request mit „HTTP/1.0 200 OK“.
Mit dem Apache HTTP server benchmarking tool lassen wir 30 Sekunden (maximal 50 000 Requests) 1 000 Requests parallel laufen um vergleichen zu können, welche Implementation, die meisten Requests pro Sekunde beantwortet:

ab -r -c 1000 -t 30 http://127.0.0.1:8088/

Twisted

#!/usr/bin/python
from twisted.web import server
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
def handle_request(env, start_response):
    status = '200 OK'
    output = 'The Output'
    response_headers = [('Content-Type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]
if __name__ == '__main__':
    resource = WSGIResource(reactor, reactor.getThreadPool(), handle_request)
    reactor.listenTCP(8088, server.Site(resource))
    reactor.run()

Requests per second: 1093.40 [#/sec] (mean)
Time per request: 0.915 [ms] (mean, across all concurrent requests)

wsgiref

#!/usr/bin/python
from wsgiref.simple_server import make_server
def handle_request(env, start_response):
    status = '200 OK'
    output = 'The Output'
    response_headers = [('Content-Type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]
if __name__ == '__main__':
    wsgiserver = make_server('', 8088, handle_request)
    wsgiserver.serve_forever()

Requests per second: 1610.80 [#/sec] (mean)
Time per request: 0.621 [ms] (mean, across all concurrent requests)

gevent

#!/usr/bin/python
from gevent.wsgi import WSGIServer
def handle_request(env, start_response):
    status = '200 OK'
    output = 'The Output'
    response_headers = [('Content-Type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)
    return [output]
if __name__ == '__main__':
    WSGIServer(('', 8088), handle_request, spawn=None).serve_forever()

Requests per second: 2121.29 [#/sec] (mean)
Time per request: 0.471 [ms] (mean, across all concurrent requests)

Die Einfachheit des Benchmark lässt natürlich keine Rückschlüsse auf Applikationen in der echten Welt zu. gevent ist aber definitiv einen Blick wert, wenn es um asynchrone Socket-Programmierung geht.

Eric Lippmann
Eric Lippmann
CTO

Eric kam während seines ersten Lehrjahres zu NETWAYS und hat seine Ausbildung bereits 2011 sehr erfolgreich abgeschlossen. Seit Beginn arbeitet er in der Softwareentwicklung und dort an den unterschiedlichen NETWAYS Open Source Lösungen, insbesondere inGraph und im Icinga Team an Icinga Web. Darüber hinaus zeichnet er für viele Kundenentwicklungen in der Finanz- und Automobilbranche verantwortlich.