OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandelnWir veröffentlichen erneut das Protokoll des Vortrags von der Konferenz HighLoad++ 2016, die vom 7. bis 8. November des letzten Jahres in Skolkovo in der Nähe von Moskau stattfand. Wladimir Protasow erklärt, wie man die Funktionalität von NGINX mit OpenResty und Lua erweitern kann.

Hallo zusammen, ich heiße Wladimir Protasow und arbeite bei Parallels. Ich erzähle ein bisschen über mich. Drei Viertel meines Lebens habe ich damit verbracht, Code zu schreiben. Ich bin Programmierer durch und durch: Manchmal träume ich im Schlaf von Code. Das letzte Viertel meines Lebens widmete ich der industriellen Entwicklung, dem Schreiben von Code, der direkt in die Produktion geht. Der Code, den einige von Ihnen verwenden, ohne es zu wissen.

Um zu verstehen, wie schlecht alles war. Als ich ein kleiner Junior war, kam ich und bekam solche zweiterabyte großen Datenbanken. Heute haben alle hier Highload. Ich ging zu Konferenzen und fragte: „Leute, erzählt mir, ihr habt Big Data, das ist toll? Wie groß sind eure Datenbanken?“ Man antwortete mir: „Wir haben 100 Gigabyte!“ Ich sagte: „Cool, 100 Gigabyte!“ Aber innerlich dachte ich, wie ich meinen Pokerface bewahren könnte. Du denkst, ja, die Leute sind toll, und dann kommst du zurück und kämpfst mit diesen vielen Terabyte großen Datenbanken. Und das als Junior. Könnt ihr euch vorstellen, wie hart das war?

Ich kenne mehr als 20 Programmiersprachen. Das ist das, womit ich mich während meiner Arbeit auseinandersetzen musste. Du bekommst Code in Erlang, C, C++, Lua, Python, Ruby und was auch immer, und musst damit arbeiten. Insgesamt musste ich es schaffen. Eine genaue Anzahl konnte ich nicht feststellen, aber irgendwie habe ich die Zahl von 20 verloren.

Da alle Anwesenden wissen, was Parallels ist und womit wir beschäftigt sind, werde ich nicht darüber sprechen, wie toll wir sind und was wir tun. Ich sage nur, dass wir 13 Büros weltweit haben, über 300 Mitarbeiter, Entwicklung in Moskau, Tallinn und Malta. Wenn man will, kann man nach Malta ziehen, wenn es im Winter kalt ist und man sich aufwärmen möchte.

Konkret unser Team schreibt in Python 2. Wir sind im Geschäft tätig und haben keine Zeit, moderne Technologien zu implementieren, deshalb leiden wir. Wir nutzen Django, weil es alles hat, und haben das Überflüssige einfach weggelassen. Außerdem MySQL, Redis und NGINX. Darüber hinaus haben wir viele andere coole Dinge. Wir haben MongoDB, wir haben Kaninchen, wir haben alles Mögliche — aber das gehört nicht zu meinem Bereich, und ich beschäftige mich nicht damit.

OpenResty

Ich habe über mich erzählt. Lassen Sie uns herausfinden, worüber ich heute sprechen werde:

  • Was ist OpenResty und wofür wird es verwendet?
  • Warum noch ein Fahrrad erfinden, wenn wir Python, NodeJS, PHP, Go und andere coole Sachen haben, mit denen alle zufrieden sind?
  • Und ein paar Beispiele aus dem Leben. Ich musste meinen Vortrag stark kürzen, da er auf 3,5 Stunden ausgeartet ist, daher werden die Beispiele spärlich sein.

OpenResty ist NGINX. Dank ihm haben wir einen vollwertigen Webserver, der gut geschrieben ist und schnell arbeitet. Ich denke, die meisten von uns verwenden NGINX in der Produktion. Ihr wisst alle, dass es schnell und cool ist. Es bietet großartigen synchronen Input/Output, sodass wir nichts neu erfinden müssen, wie man es in Python mit gevent gemacht hat. Gevent ist cool und großartig, aber wenn ihr C-Code schreibt und etwas schiefgeht, werdet ihr mit gevent verrückt beim Debuggen. Ich hatte die Erfahrung: Es hat zwei ganze Tage gedauert herauszufinden, was schiefgelaufen ist. Wenn vorher nicht jemand einige Wochen lang geforscht hätte, das Problem gefunden und im Internet darüber geschrieben hätte und Google es nicht gefunden hätte, wären wir total verzweifelt gewesen.

In NGINX sind bereits Caching und statische Inhalte implementiert. Ihr müsst euch keine Gedanken darüber machen, wie das ordentlich geht, damit es nirgendwo hakt und ihr keine Deskriptoren verliert. Nginx lässt sich sehr praktisch deployen, ihr müsst nicht darüber nachdenken, was ihr nehmen sollt – WSGI, PHP-FPM, Gunicorn oder Unicorn. Nginx installieren, den Admins übergeben, sie wissen, wie man damit umgeht. Nginx verarbeitet Anfragen strukturiert. Dazu werde ich später etwas sagen. Kurz gesagt, es gibt eine Phase, in der es die Anfrage nur angenommen hat, eine, in der es die Anfrage bearbeitet hat, und eine, in der es den Inhalt an den Benutzer übergeben hat.

Nginx ist großartig, aber es gibt ein Problem: Es ist nicht flexibel genug, auch bei all den coolen Features, die die Leute in die Konfiguration gepackt haben, obwohl es viel anpassbar ist. Diese Kraft reicht nicht aus. Deshalb haben die Leute bei Taobao vor vielen Jahren, ich glaube vor acht Jahren, Lua integriert. Was bringt das?

  • Größe. Es ist klein. LuaJIT hat einen Overhead von etwa 100-200 Kilobyte im Speicher und einen minimalen Overhead in der Leistung.
  • Geschwindigkeit. Der Interpreter LuaJIT ist in vielen Situationen nahe an C, in einigen Situationen schlägt er Java und in anderen überholt er sie. Eine Zeit lang galt er als State of the Art, der leistungsstärkste JIT-Compiler. Jetzt gibt es leistungsstärkere, aber sie sind sehr schwerfällig, wie zum Beispiel V8. Einige JS-Interpreter und der Java HotSpot sind in bestimmten Punkten schneller, aber in anderen Bereichen verlieren sie immer noch.
  • Einfachheit beim Erlernen. Wenn Sie beispielsweise einen Codebestand in Perl haben und nicht Booking sind, werden Sie keine Perl-Programmierer finden. Denn die gibt es nicht, sie wurden alle weggenommen, und sie zu unterrichten ist langwierig und kompliziert. Wenn Sie Programmierer in etwas anderem wollen, müssen diese möglicherweise auch umgeschult oder gefunden werden. Bei Lua ist alles einfach. Ein Junior kann Lua in drei Tagen lernen. Ich habe etwa zwei Stunden gebraucht, um mich zurechtzufinden. Nach zwei Stunden habe ich bereits Produktionscode geschrieben. Nach ungefähr einer Woche war es direkt in der Produktion.

Das Ergebnis sieht so aus:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Hier gibt es viel. In OpenResty wurden eine Menge Module gesammelt, sowohl Lua- als auch Engine-Module. Und Sie haben alles bereit – einfach deployen und es funktioniert.

Beispiele

Genug der Lyrik, kommen wir zum Code. Hier ist ein kleines Hello World:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Was gibt es hier? Das ist ein engine-standort. Wir müssen uns keine Gedanken über Routing machen, wir nehmen kein fertiges – wir haben bereits etwas in NGINX, wir leben gut und bequem.

content_by_lua_block – das ist ein Block, der sagt, dass wir Inhalte mit einem Lua-Skript bereitstellen. Wir nehmen die engine-Variable remote_addr und stecken sie in string.format. Das ist dasselbe wie sprintf, nur auf Lua, nur richtig. Und wir geben es dem Clienten.

Das Ergebnis wird so aussehen:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Aber kehren wir in die reale Welt zurück. In der Produktion wird niemand Hello World deployen. Unsere Anwendung greift normalerweise auf eine Datenbank oder woanders zu und wartet die meiste Zeit auf eine Antwort.

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Es sitzt einfach da und wartet. Das ist nicht besonders gut. Wenn 100.000 Benutzer kommen, wird es sehr schwierig. Daher lassen Sie uns als Beispiel eine einfache Anwendung aufbauen. Wir werden nach Bildern suchen, zum Beispiel von Katzen. Aber wir werden nicht einfach so suchen, wir werden die Schlüsselwörter erweitern, und wenn der Benutzer nach "Kätzchen" sucht, werden wir ihm Katzen, Flauschige und Ähnliches finden. Zuerst müssen wir die Anfrage-Daten im Backend erhalten. Das sieht so aus:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Zwei Zeilen ermöglichen es Ihnen, die GET-Parameter abzurufen, keine Schwierigkeiten. Dann bekommen wir, sagen wir mal, aus der Datenbank mit einer Tabelle für Schlüsselwörter und Erweiterungen diese Informationen mit einer gewöhnlichen SQL-Abfrage. Es ist ganz einfach. Das sieht so aus:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir binden die Bibliothek ein resty.mysql, die bereits in unserem Paket enthalten ist. Wir müssen nichts installieren, alles ist bereit. Wir geben an, wie wir uns verbinden, und machen die SQL-Abfrage:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Es ist ein bisschen gruselig hier, aber alles funktioniert. Hier ist 10 – das ist das Limit. Wir ziehen 10 Einträge heraus, wir sind faul und wollen nicht mehr anzeigen. In SQL habe ich das Limit vergessen.

Dann finden wir Bilder zu allen Anfragen. Wir sammeln eine Menge Anfragen und füllen eine Lua-Tabelle aus, die heißt reqs, und machen ngx.location.capture_multi.

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Alle diese Anfragen laufen parallel ab und wir bekommen die Antworten zurück. Die Arbeitszeit ist gleich der Antwortzeit des langsamsten. Wenn alles innerhalb von 50 Millisekunden abläuft und wir hundert Anfragen gesendet haben, kommt die Antwort nach 50 Millisekunden.

Da wir faul sind und keine HTTP-Verarbeitung und Caching schreiben wollen, lassen wir NGINX alles für uns erledigen. Wie Sie gesehen haben, gab es eine Anfrage an url/fetch, hier ist sie:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir machen eine einfache proxy_pass, geben an, wo wir cachen wollen, wie das gemacht wird, und alles funktioniert.

Aber das reicht nicht, wir müssen auch die Daten an den Benutzer zurückgeben. Die einfachste Idee ist, alles in JSON zu serialisieren, ganz einfach, in zwei Zeilen. Wir geben den Content-Type an und geben JSON zurück.

Aber es gibt eine Schwierigkeit: Der Benutzer möchte kein JSON lesen. Wir müssen Frontend-Entwickler anziehen. Manchmal haben wir anfangs keine Lust dazu. Und die SEOs werden sagen, dass es ihnen egal ist, wenn wir Bilder suchen. Aber wenn wir ihnen irgendwelche Inhalte liefern, werden sie sagen, dass unsere Suchmaschinen nichts indexieren.

Was tun wir damit? Natürlich werden wir dem Benutzer HTML zurückgeben. Es wäre nicht klug, alles manuell zu generieren, also wollen wir Templates verwenden. Dafür gibt es die Bibliothek lua-resty-template.

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Sie haben wahrscheinlich die drei gruseligen Buchstaben OPM gesehen. OpenResty kommt mit einem eigenen Paketmanager, über den man viele verschiedene Module installieren kann, darunter lua-resty-template. Es ist eine einfache Template-Engine, die ähnlich wie Django-Templates ist. Dort kann man Code schreiben und Variablen einsetzen.

Infolgedessen wird alles ungefähr so aussehen:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir haben die Daten genommen und das Template ebenfalls in zwei Zeilen gerendert. Der Benutzer ist glücklich und hat Katzen bekommen. Da wir die Anfrage erweitert haben, hat er auch eine Seehund bekommen. Wer weiß, vielleicht hat er genau danach gesucht, konnte aber seine Anfrage nicht richtig formulieren.

Alles toll, aber wir sind im Development und wollen es den Benutzern noch nicht zeigen. Lass uns eine Authentifizierung einrichten. Um das zu tun, schauen wir uns an, wie NGINX die Anfrage in Bezug auf OpenResty verarbeitet:

  • Die erste Phase ist -Token und, wenn der Benutzer neu eingetroffen ist und wir ihn anhand von Kopfzeilen, IP-Adresse und weiteren Daten betrachtet haben. Wir können ihn sofort abweisen, falls er uns nicht gefällt. Dies kann für die Authentifizierung verwendet werden, oder wenn wir sehr viele Anfragen erhalten, können wir sie in dieser Phase einfach abweisen.
  • rewrite. Wir schreiben einige Anfragedaten um.
  • für Inhalte mit Bildern (alternativer Text. Wir geben den Inhalt an den Benutzer zurück.
  • headers filter. Wir ersetzen die Antwortköpfe. Wenn wir proxy_pass, können wir einige Kopfzeilen umschreiben, bevor wir sie an den Benutzer ausgeben.
  • body filter. Wir können den Inhalt des Körpers ändern.
  • log — Protokollierung. Man kann Protokolle in Elasticsearch ohne zusätzliche Schicht schreiben.

Unsere Authentifizierung wird ungefähr so aussehen:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir fügen dies zu dem Standort, das wir zuvor beschrieben haben, hinzu und packen diesen Code hinein:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir prüfen, ob wir ein Cookie-Token haben. Wenn nicht, leiten wir zur Authentifizierung weiter. Benutzer sind schlau und können erraten, dass sie ein Cookie-Token setzen müssen. Daher speichern wir es auch in Redis:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Der Code zur Arbeit mit Redis ist sehr einfach und unterscheidet sich nicht von anderen Sprachen. Auch die gesamte Ein-/Ausgabe ist nicht blockierend. Wenn Sie synchronen Code schreiben, funktioniert es asynchron. Ähnlich wie bei gevent, nur gut umgesetzt.

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Lassen Sie uns die Authentifizierung selbst erstellen:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Wir sagen, dass wir den Körper der Anfrage lesen müssen. Wir erhalten POST-Parameter und überprüfen, ob Benutzername und Passwort korrekt sind. Wenn sie falsch sind, leiten wir zur Authentifizierung weiter. Wenn sie correct sind, speichern wir das Token in Redis:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Vergessen Sie nicht, das Cookie zu setzen, das wird auch in zwei Zeilen gemacht:

OpenResty: NGINX in einen vollwertigen Anwendungsserver verwandeln

Dies ist ein einfaches, hypothetisches Beispiel. Natürlich werden wir keinen Dienst erstellen, der Katzenbilder anzeigt. Aber wer weiß schon. Deshalb lassen Sie uns betrachten, was wir in der Produktion tun können.

  • Minimalistisches Backend. Manchmal müssen wir dem Backend nur sehr wenige Daten bereitstellen: irgendwo ein Datum einsetzen, irgendwo eine Liste ausgeben, sagen, wie viele Benutzer gerade auf der Website sind, einen Zähler oder Statistiken einfügen. Etwas Kleines. Minimale Teile können sehr einfach gemacht werden. Das geht schnell, einfach und gut.
  • Datenvorverarbeitung. Manchmal wollen wir Werbung auf unsere Seite einfügen, und diese Werbung holen wir über API-Anfragen. Das lässt sich hier sehr leicht umsetzen. Wir belasten nicht unseren Backend, der ohnehin schon schwer arbeitet. Man kann es hier zusammenstellen. Wir können einige JS-Skripte anpassen oder, umgekehrt, etwas auseinandernehmen, was preprocessieren, bevor wir es dem Benutzer zurückgeben.
  • Frontend für Microservices. Das ist auch ein sehr gutes Beispiel, das ich umgesetzt habe. Zuvor habe ich in der Firma Tenzor gearbeitet, die sich mit elektronischer Berichterstattung beschäftigt und etwa die Hälfte der juristischen Personen im Land mit Berichten versorgt. Wir haben einen Service erstellt, bei dem viele Dinge mithilfe des gleichen Mechanismus realisiert wurden: Routing, Autorisierung und mehr.
    OpenResty kann als Kleber für Ihre Microservices dienen, der einen einheitlichen Zugang zu allem und eine einheitliche Schnittstelle gewährleistet. Da Microservices in so unterschiedlichen Technologien geschrieben sein können, wie hier Node.js, hier PHP, hier Python und hier etwas auf Erlang, wissen wir, dass wir nicht denselben Code überall umschreiben möchten. Daher kann OpenResty im Frontend eingesetzt werden.
  • Statistik und Analytik. Üblicherweise steht NGINX am Eingang, und alle Anfragen laufen darüber. An diesem Punkt lässt sich sehr bequem alles zusammenfassen. Man kann etwas sofort berechnen und irgendwohin weiterleiten, zum Beispiel an Elasticsearch, Logstash oder einfach in ein Log schreiben und später woandershin senden.
  • Mehrbenutzersysteme. Zum Beispiel lassen sich Online-Spiele sehr gut umsetzen. Heute wird Alexander Gladys in Kapstadt darüber sprechen, wie man ein Mehrbenutzerspiel schnell mit OpenResty prototypisieren kann.
  • Anfragen filtern (WAF). Derzeit ist es angesagt, verschiedene Web Application Firewalls zu erstellen; es gibt viele Dienste, die diese anbieten. Mit OpenResty kann man sich eine Web Application Firewall bauen, die einfach und problemlos Anfragen nach Ihren Anforderungen filtert. Wenn Sie mit Python arbeiten, wissen Sie, dass PHP Ihnen nicht injiziert wird, es sei denn, Sie starten es aus der Konsole irgendwo. Sie wissen, dass Sie MySQL und Python haben. Wahrscheinlich könnten hier Versuche unternommen werden, eine Directory Traversal durchzuführen und etwas in die Datenbank zu injizieren. Daher kann man verdächtige Anfragen schnell und kostengünstig gleich im Frontend filtern.
  • Gemeinschaft. Da OpenResty auf NGINX basiert, hat es den Bonus – das ist NGINX-Community. Es ist sehr groß, und ein erheblicher Teil der Fragen, die Sie anfangs haben werden, wurde bereits von der NGINX-Community gelöst.

    Lua-Entwickler. Gestern habe ich mich mit den Leuten unterhalten, die zu dem Schulungstag HighLoad++ gekommen sind und habe gehört, dass nur Tarantool in Lua geschrieben ist. Das ist nicht richtig, in Lua ist eine Menge geschrieben. Beispiele: OpenResty, der XMPP-Server Prosody, die Spiele-Engine Love2D, Lua-Skripte in Warcraft und anderen Orten. Es gibt sehr viele Lua-Entwickler, sie haben eine große und hilfsbereite Community. Alle meine Fragen zu Lua wurden innerhalb weniger Stunden geklärt. Wenn man in die Mailingliste schreibt, hat man buchstäblich nach wenigen Minuten schon eine Menge Antworten, die genau erklären, was und wie, was was ist. Das ist wirklich großartig. Leider gibt es nicht überall eine so freundliche Community.
    Zu OpenResty gibt es GitHub, dort kann man ein Issue erstellen, wenn etwas kaputt ist. Es gibt eine Mailingliste auf Google Groups, wo man allgemeine Fragen diskutieren kann, es gibt eine Liste auf Chinesisch — falls Sie vielleicht kein Englisch sprechen, aber Chinesischkenntnisse haben.

Ergebnisse

  • Ich hoffe, ich konnte vermitteln, dass OpenResty ein sehr praktisches Framework ist, das für das Web optimiert ist.
  • Es hat eine niedrige Einstiegshürde, da der Code ähnlich ist wie der, den wir verwenden, die Sprache relativ einfach und minimalistisch ist.
  • Es bietet asynchrones I/O ohne Callbacks, wir haben keine 'Nudeln', wie wir das manchmal in NodeJS machen können.
  • Es hat ein einfaches Deployment, da wir nur NGINX mit dem entsprechenden Modul und unseren Code benötigen, und alles funktioniert sofort.
  • Eine große und hilfsbereite Community.

Ich habe nicht im Detail erzählt, wie das Routing funktioniert, das wäre eine sehr lange Geschichte geworden.

Vielen Dank für Ihre Aufmerksamkeit!

Video abspielen

Wladimir Protasov — OpenResty: Wir verwandeln NGINX in einen vollwertigen Anwendungsserver

Quelle: habr.com
60GB SSD 8Gb DDR4