In der Open-Source-Welt gibt es täglich so viele neue, interessante Projekte, nach denen es sich lohnt, Ausschau zu halten. Meist sind es Projekte US-amerikanischen Ursprungs, denen die große Aufmerksamkeit zuteil wird, während Projekte aus anderen Ländern zum Teil eher unbekannt sind. Das können deutsche Projekte sein, aber vor allem französische oder chinesische Projekte sind hierzulande oft unbekannt. Ein Beispiel dafür ist der chinesische Fork des Webservers Nginx (der ja seinerseits russischen Ursprungs ist), den wir in [1] vorgestellt haben. Ein weiteres Projekt aus dem Umfeld des "chinesischen Amazon" Taobao ist die Nginx-Distribution OpenResty [2], die fast im Alleingang von Yichun "Agent" Zhang entwickelt wurde. Er hat in OpenResty den Nginx-Server mit vielen nützlichen Modulen gebündelt, die er zum Großteil selbst geschrieben hat und die aus Nginx einen superschnellen Web-Application-Server machen.
Den Großteil von OpenResty machen Module für die Skriptsprache Lua aus, die als eingebettete Sprache wegen ihres geringen Ressourcenverbrauchs und ihrer guten Performance beliebt ist. Auch der Apache-Webserver hat mittlerweile das in ADMIN 03/2012 vorgestellte Mod-Lua in die Kerndistribution aufgenommen [3]. Weitere Beispiele für die Beliebtheit von Lua und eine Einführung in die Sprache sind im Programmieren-Teil dieses Hefts zu finden. Die OpenResty-Module erlauben es, schon in den Konfigurationsdateien Lua-Anweisungen zu verwenden. Das erlaubt komplexe und dynamische Konfigurationen, mit denen sich zum Beispiel Web-Services implementieren lassen. Mitgeliefert werden etwa Module zur Anbindung an Memcache und Datenbanken wie MySQL/Drizzle, PostgreSQL und Redis.
Mit OpenResty können Lua-Skripts in alle Phasen der Verarbeitung eines HTTP-Requests eingreifen, sie verändern, Antworten erstellen und Header verändern. Dies alles passiert in recht hoher Geschwindigkeit, sodass die Kombination Lua und Nginx, namentlich OpenResty, unter den ersten Plätzen der Web-Framework-Benchmarks von TechEmpower zu finden ist (Abbildung 1). Der Grund dafür liegt in der Event-basierten Verarbeitung von Requests, die Nginx auszeichnet, und die sich dank OpenResty auch mit Lua-Code nutzen lässt. Noch schneller geht es mit dem Lua-Just-in-Time-Compiler (LuaJIT), der Teil von OpenResty ist.
Den gängigen Linux-Distributionen liegt OpenResty nicht bei, Sie müssen es also aus dem Quellcode selbst übersetzen. Dafür müssen einige Pakete und Bibliotheken installiert sein, nämlich Perl 5.6.1 oder neuer, Libreadline, Libpcre und LibSSL. Dann laden Sie das Quellcode-Paket von der OpenResty-Website herunter, entpacken es und führen »./configure
«
aus, am besten mit der Option »--with-luajit
«
. Per Default sind außerdem die Module für die Drizzle-Datenbank, PostgreSQL und Iconv ausgeschaltet. Aktivieren Sie sie mit den passenden Configure-Optionen, wenn Sie sie brauchen. Mehr verrät »./configure --help
«
. Installiert werden die Binärdateien und die Module mit »make install
«
. Allerdings gibt es kein »make install
«
, weshalb es sich empfiehlt, zum Beispiel das Checkinstall-Paket zu installieren und dann OpenResty mit »checkinstall make install
«
zu installieren. Die installierten Dateien lassen sich dann über den Paketmanager der Distribution wieder entfernen.
Per Default werden die Nginx-Dateien in »/usr/local/openresty
«
installiert. Wer das nicht will, setzt mit der Configure-Option »--prefix
«
ein anderes Verzeichnis. Die Default-Einstellung ist aber nicht schlecht, denn sie kollidiert nicht mit eventuell installierten Nginx-Paketen einer Linux-Distribution. Bleibt noch, den Nginx-Server beim Booten zu starten, was die beiden Konfigurationsdateien in Listing 1 (Upstart) und Listing 2 (Systemd) übernehmen. Ein Aufruf von »nginx -t
«
überprüft die Konfiguration, bevor der Serverprozess startet. Im Fehlerfall verschafft »nginx -V
«
einen Überblick über die vorhandenen Module.
Listing 2
/lib/systemd/system/nginx.service
01 [Unit] 02 Description=OpenResty Stack for Nginx 03 After=syslog.target network.target remote-fs.target nss-lookup.target 04 05 [Service] 06 Type=forking 07 PIDFile=/var/run/nginx.pid 08 ExecStartPre=/usr/local/openresty/nginx/sbin/nginx -t 09 ExecStart=/usr/local/openresty/nginx/sbin/nginx 10 ExecReload=/bin/kill -s HUP $MAINPID 11 ExecStop=/bin/kill -s QUIT $MAINPID 12 PrivateTmp=true 13 14 [Install] 15 WantedBy=multi-user.target
Listing 1
/etc/init/openresty.conf
01 # openresty 02 03 description "nginx openresty stack" 04 author "Oliver Frommel <ofrommel@domain.com>" 05 06 start on (filesystem and net-device-up IFACE=lo) 07 stop on runlevel [!2345] 08 09 env DAEMON=/usr/local/openresty/nginx/sbin/nginx 10 env PID=/var/run/nginx.pid 11 12 expect fork 13 respawn 14 respawn limit 10 5 15 16 pre-start script 17 $DAEMON -t 18 if [ $? -ne 0 ] 19 then exit $? 20 fi 21 end script 22 23 exec $DAEMON
Übrigens gibt es auch Module für einschlägige Software zum Konfigurationsmanagement, mit denen sich OpenResty auf Servern deployen lässt. So liegen auf Github einige Module für Puppet, ein Chef-Kochrezept ist direkt auf der Opscode-Website zu finden. Das Chef-Modul wird gut gepflegt und wurde zuletzt Anfang November 2013 aktualisiert.
Im einfachsten Fall lässt sich nun Lua-Code über das Schlüsselwort »content_by_lua
«
direkt in die Konfigurationsdateien einbetten, etwa so:
content_by_lua ' ngx.say("<p>test</p>") ';
Aus einer Datei liest Nginx den Lua-Code über »content_by_lua_file
«
. Analog dazu übernimmt Lua auch das Umschreiben von Requests (Rewrite) oder die Authentifizierung. Die dafür vorgesehenen Anweisungen, die Lua-Code aus einer Datei laden, heißen »rewrite_by_lua
«
und »content_by_lua
«
. Dabei lädt der Webserver normalerweise den Lua-Code nur einmal ein, was klarerweise den Overhead reduziert. Abschalten lässt sich dies, etwa bei der Entwicklung von Skripts, indem man »lua_code_cache
«
auf »off
«
setzt.
Im Prinzip geht es sogar auch ganz ohne Lua-Code, wie ein Beispiel von Richard Nyström zeigt [5], der einen Web-Service nur mit SQL-Anweisungen in der Nginx-Konfiguration realisiert. Zum Beispiel liefert der folgende Ausschnitt der Konfiguration bei einem Get-Request alle Artikel einer Datenbank zurück:
postgres_query HEAD GET "SELECT * FROM articles";
Oft liegen die interessanten Daten nicht direkt auf dem Webserver, sondern sind auf andere Rechner ausgelagert, wo zum Beispiel ein Verzeichnisdienst, eine Datenbank oder Ähnliches laufen. OpenResty unterstützte solche Serviceorientierten Architekturen (SOA) über sogenannte Subrequests. Ein eingehender HTTP-Requests löst also weitere Requests aus, die fehlende Daten einholen, entweder von anderen Rechnern oder von Diensten auf demselben Server, auf dem auch Nginx läuft. Ein Beispiel dafür ist das Auth-Request-Modul, das Subrequests zur Authentifizierung verwendet. So lässt sich der Zugriffsschutz modular gestalten und verschiedene Dienste miteinander verbinden:
location /private/ { auth_request /auth; ... }
Selbst parallele Subrequests können abgesetzt werden, wenn man den Aufruf »capture_multi
«
verwendet. Auf einer niedrigeren Ebene angesiedelt ist ein Interface, um direkt mit Netzwerk-Sockets zu arbeiten. Es entspricht der TCP-API von Lua, arbeitet aber per Default nichtblockierend.
Auch Realtime-Webanwendungen sind mit OpenResty kein Problem. Sie lassen sich zum Beispiel mit dem Websocket-Support verwenden, den Nginx in der stabilen Version 1.4.0 hinzugewonnen hat [6]. Allerdings wird Nginx damit nur zum Websocket-Proxy. Um die Verarbeitung der Daten muss sich jemand anders kümmern. Mit OpenResty kann das zum Beispiel ein Lua-Modul übernehmen. Weil Socket-Programmierung damit relativ unaufwendig ist, braucht es dafür nur wenige Zeilen Code. Wie das geht, hat Aapo Talvensaari in seinem Blog [7] demonstriert.
Ein recht interessantes Modul, das sich für Debugging oder die Entwicklung von Webanwendungen oder -diensten gut eignet, ist »ngx_echo
«
, das das Konfigurationsvokabular um Shell-artige Anweisungen wie »echo
«
, »sleep
«
, »time
«
und »exec
«
erweitert. Damit lassen sich URLs einrichten, um Clients zu testen, ohne dass auf dem Server gleichzeitig fehleranfälliger Code geschrieben werden muss. Das folgende Beispiel zeigt eine Anwendung:
location /echodelay { echo hello; echo_flush; echo_sleep 2.5; echo world; }
Zuerst gibt der Codeblock den String »hello
«
aus, der durch das Leeren des Output-Puffers mit »echo_flush
«
sofort sichtbar wird. Danach wartet der Server 2,5 Sekunden lang, bevor er mit der Ausgabe von »world
«
weitermacht.
Die Beschreibung der vielen Möglichkeiten, die OpenResty über die Programmierung mit Lua bietet, würde den Rahmen des Artikels sprengen. Tabelle 1 gibt aber einen Überblick über alle mitgelieferten Module.
Tabelle 1
OpenResty-Module
Modul | Funktion |
---|---|
ArrayVarNginxModule |
Array-Variablen für Nginx-Konfigurationsdateien |
AuthRequestNginxModule |
Authentifizierung mit Subrequests |
CoolkitNginxModule |
Sammlung kleiner Nginx-Addons |
DrizzleNginxModule |
Anbindung an Drizzle- und MySQL-Datenbank |
EchoNginxModule |
Shell-artige Utilities für Debugging |
EncryptedSessionNginxModule |
Verschlüsselte Session-Daten |
FormInputNginxModule |
Verarbeitung von Formularen in Nginx-Konfiguration |
HeadersMoreNginxModule |
Erweiterte Header-Verarbeitung |
IconvNginxModule |
Konvertierung von Zeichensätzen |
StandardLuaInterpreter |
Standard-Lua-Interpreter |
MemcNginxModule |
Memcache-Protokoll |
Nginx |
Nginx-Distribution |
NginxDevelKit |
Nginx-SDK |
LuaCjsonLibrary |
Schnelles JSON-Modul für Lua |
LuaJIT |
Just-in-Time-Compiler für Lua |
LuaNginxModule |
Lua-Modul für Nginx |
LuaRdsParserLibrary |
Parser für Resty-DBD-Stream von Datenbankmodulen |
LuaRedisParserLibrary |
Parser für Redis-Antworten |
LuaRestyDNSLibrary |
DNS-Bibliothek |
LuaRestyLockLibrary |
Nichtblockierende Mutex-Locks |
LuaRestyMemcachedLibrary |
Treiber für Memcache |
LuaRestyMySQLLibrary |
Treiber für MySQL |
LuaRestyRedisLibrary |
Treiber für Redis |
LuaRestyStringLibrary |
String-Bibliothek |
LuaRestyUploadLibrary |
Bibliothek für HTTP-Uploads |
LuaRestyWebSocketLibrary |
Bibliothek für Websockets |
PostgresNginxModule |
Anbindung an PostgreSQL |
RdsCsvNginxModule |
Konvertiert Resty-DBD-Streams ins CSV-Format |
RdsJsonNginxModule |
Konvertiert Resty-DBD-Streams nach JSON |
RedisNginxModule |
Redis-Modul, liefert verarbeitete Antworten |
Redis2NginxModule |
Redis-Modul, liefert unverarbeitete Antworten |
SetMiscNginxModule |
Diverse Einstellungen (MD5, JSON, …) |
SrcacheNginxModule |
Transparentes Caching |
XssNginxModule |
Support für Cross-Site-Ajax-Requests |