BLOG | NGINX

Mit dem NGINX JavaScript-Modul die Leistung und Benutzerfreundlichkeit von JavaScript für jede Anfrage nutzen

NGINX-Teil-von-F5-horiz-schwarz-Typ-RGB
Liam Crilly Miniaturbild
Liam Crilly
Veröffentlicht am 23. April 2021

Editor – Der Blogbeitrag mit dem Titel „Einführung in das NGINX JavaScript-Modul“ leitet hierher weiter. Der Beitrag wurde mit den Anweisungen und Funktionen des NGINX JavaScript-Moduls aktualisiert, die ab April 2021 unterstützt werden.

Das NGINX JavaScript-Modul (njs) wurde allgemein als stabiles Modul in NGINX Open Source 1.11.10 und NGINX Plus R12 verfügbar. [Das Modul hieß ursprünglich nginScript, und dieser Name erscheint in einigen älteren Beiträgen.] Wir haben seit der Einführung im September 2015 kontinuierlich an NGINX JavaScript gearbeitet und die im stabilen Modul enthaltenen Funktionen und Sprachunterstützung hinzugefügt.

NGINX JavaScript ist eine einzigartige JavaScript-Implementierung für NGINX und NGINX Plus, die speziell für serverseitige Anwendungsfälle und die Verarbeitung einzelner Anfragen entwickelt wurde. Es erweitert die NGINX-Konfigurationssyntax mit JavaScript-Code, um anspruchsvolle Konfigurationslösungen zu implementieren.

Die Anwendungsfälle sind umfangreich, insbesondere da das NGINX-JavaScript-Modul sowohl für das HTTP- als auch das TCP/UDP-Protokoll verfügbar ist. Zu den Beispielanwendungsfällen für NGINX JavaScript gehören:

Bevor wir uns ausführlicher mit NGINX-JavaScript befassen, wollen wir zunächst zwei häufige Missverständnisse ausräumen.

NGINX JavaScript ist nicht Lua

Die NGINX-Community hat im Laufe der Jahre mehrere programmgesteuerte Erweiterungen erstellt. Zum Zeitpunkt des Schreibens ist Lua das beliebteste davon; es ist als Modul für NGINX und als unterstütztes, vorgefertigtes dynamisches Modul eines Drittanbieters für NGINX Plus verfügbar. Das Lua-Modul und die Zusatzbibliotheken ermöglichen eine tiefe Integration mit dem NGINX-Kern und einen umfangreichen Funktionsumfang, darunter einen Treiber für Redis.

Lua ist eine leistungsstarke Skriptsprache. Allerdings bleibt es im Hinblick auf die Akzeptanz eher eine Nische und ist normalerweise nicht im „Skillset-Toolkit“ von Frontend-Entwicklern oder DevOps-Ingenieuren zu finden.

NGINX-JavaScript soll Lua nicht ersetzen und es wird einige Zeit dauern, bis NGINX-JavaScript über einen vergleichbaren Funktionsumfang verfügt. Das Ziel von NGINX JavaScript besteht darin, einer möglichst breiten Community durch die Verwendung einer beliebten Programmiersprache programmgesteuerte Konfigurationslösungen bereitzustellen.

NGINX JavaScript ist nicht Node.js

NGINX JavaScript zielt nicht darauf ab, NGINX oder NGINX Plus in einen Anwendungsserver zu verwandeln. Vereinfacht ausgedrückt ähneln die Anwendungsfälle für NGINX JavaScript Middleware, da die Ausführung des JavaScript-Codes zwischen dem Client und dem Inhalt erfolgt. Technisch gesehen hat Node.js zwar zwei Dinge mit der Kombination aus NGINX JavaScript und NGINX oder NGINX Plus gemeinsam – eine ereignisgesteuerte Architektur und die Programmiersprache JavaScript –, doch damit enden die Ähnlichkeiten auch schon.

Node.js verwendet die JavaScript-Engine Google V8, während NGINX-JavaScript eine maßgeschneiderte Implementierung der ECMAScript-Standards ist, die speziell für NGINX und NGINX Plus entwickelt wurde. Node.js verfügt über eine persistente JavaScript-virtuelle Maschine (VM) im Speicher und führt zur Speicherverwaltung routinemäßig eine Garbage Collection durch, während NGINX-JavaScript für jede Anfrage eine neue JavaScript-VM und den erforderlichen Speicher initialisiert und den Speicher freigibt, wenn die Anfrage abgeschlossen ist.

JavaScript als serverseitige Sprache

Wie oben erwähnt, ist NGINX JavaScript eine maßgeschneiderte Implementierung der JavaScript-Sprache. Alle anderen vorhandenen JavaScript-Runtime-Engines sind für die Ausführung in einem Webbrowser konzipiert. Die clientseitige Codeausführung unterscheidet sich in vielerlei Hinsicht von der serverseitigen Codeausführung – von der Verfügbarkeit der Systemressourcen bis hin zur möglichen Anzahl gleichzeitiger Laufzeiten.

Wir haben uns entschieden, unsere eigene JavaScript-Laufzeitumgebung zu implementieren, um die Anforderungen der serverseitigen Codeausführung zu erfüllen und eine elegante Integration in die Anforderungsverarbeitungsarchitektur von NGINX zu erzielen. Unsere Designprinzipien für NGINX JavaScript sind folgende:

  • Laufzeitumgebung lebt und stirbt mit der Anfrage

    Das NGINX-JavaScript-Modul verwendet eine einthreadige Bytecode-Ausführung, die für eine schnelle Initialisierung und Entsorgung ausgelegt ist. Die Laufzeitumgebung wird pro Anfrage initialisiert. Der Start erfolgt extrem schnell, da keine komplexen Zustände oder Helfer initialisiert werden müssen. Der Speicher wird während der Ausführung in Pools angesammelt und nach Abschluss durch Freigabe der Pools freigegeben. Durch dieses Speicherverwaltungsschema entfällt die Notwendigkeit, einzelne Objekte zu verfolgen und freizugeben oder einen Garbage Collector zu verwenden.

  • Nicht blockierende Codeausführung

    Das ereignisgesteuerte Modell von NGINX und NGINX Plus plant die Ausführung einzelner NGINX-JavaScript-Laufzeitumgebungen. Wenn eine NGINX-JavaScript-Regel eine blockierende Operation ausführt (z. B. das Lesen von Netzwerkdaten oder das Senden einer externen Unteranforderung), unterbrechen NGINX und NGINX Plus die Ausführung der zugehörigen NGINX-JavaScript-VM transparent und planen sie neu, wenn das Ereignis abgeschlossen ist. Dies bedeutet, dass Sie Regeln auf einfache, lineare Weise schreiben können und NGINX und NGINX Plus diese ohne interne Blockierung planen können.

  • Implementieren Sie nur die Sprachunterstützung, die wir brauchen

    Die Spezifikationen für JavaScript werden durch die ECMAScript -Standards definiert. NGINX JavaScript folgt ECMAScript 5.1 mit etwas ECMAScript 6 für mathematische Funktionen. Durch die Implementierung unserer eigenen JavaScript-Laufzeitumgebung haben wir die Freiheit, die Sprachunterstützung für serverseitige Anwendungsfälle zu priorisieren und zu ignorieren, was wir nicht benötigen. Wir pflegen eine Liste der aktuell unterstützten Sprachelemente .

  • Enge Integration mit den Phasen der Anforderungsverarbeitung

    NGINX und NGINX Plus verarbeiten Anfragen in unterschiedlichen Phasen. Konfigurationsanweisungen werden normalerweise in einer bestimmten Phase ausgeführt, und native NGINX-Module nutzen häufig die Möglichkeit, eine Anforderung in einer bestimmten Phase zu prüfen oder zu ändern. NGINX-JavaScript stellt einige der Verarbeitungsphasen durch Konfigurationsanweisungen bereit, um Kontrolle darüber zu ermöglichen, wann der JavaScript-Code ausgeführt wird. Diese Integration mit der Konfigurationssyntax verspricht die Leistung und Flexibilität nativer NGINX-Module mit der Einfachheit von JavaScript-Code.

    In der folgenden Tabelle ist angegeben, welche Verarbeitungsphasen zum Zeitpunkt des Schreibens über NGINX-JavaScript zugänglich sind und welche Konfigurationsanweisungen dies ermöglichen.

    Verarbeitungsphase HTTP-Modul Stream-Modul
    Zugriff – Authentifizierung und Zugriffskontrolle auth_request und js_content js_zugriff
    Pre-Read – Nutzlast lesen/schreiben N / A js_preread
    Filter – Lese-/Schreibantwort während Proxy js_body_filter
    js_header_filter
    js_filter
    Inhalt – Antwort an den Client senden js_inhalt N / A
    Protokoll / Variablen – Auswertung bei Bedarf js_set js_set

Erste Schritte mit NGINX JavaScript – Ein Beispiel aus der Praxis

NGINX JavaScript ist als Modul implementiert, das Sie in eine NGINX Open Source-Binärdatei kompilieren oder dynamisch in NGINX oder NGINX Plus laden können. Anweisungen zum Aktivieren von NGINX JavaScript mit NGINX und NGINX Plus finden Sie am Ende dieses Artikels.

In diesem Beispiel verwenden wir NGINX oder NGINX Plus als einfachen Reverse-Proxy und nutzen NGINX-JavaScript, um Zugriffsprotokolleinträge in einem speziellen Format zu erstellen, das:

  • Enthält die vom Client gesendeten Anforderungsheader
  • Enthält die vom Backend zurückgegebenen Antwortheader
  • Verwendet Schlüssel-Wert-Paare für eine effiziente Aufnahme in und Suche mit Protokollverarbeitungstools wie dem ELK Stack (jetzt Elastic Stack), Graylog und Splunk

Die NGINX-Konfiguration für dieses Beispiel ist äußerst einfach.

 

Wie Sie sehen, ist der NGINX-JavaScript-Code nicht in die Konfigurationssyntax integriert. Stattdessen verwenden wir die js_import- Direktive, um die Datei anzugeben, die unseren gesamten JavaScript-Code enthält. Die Direktive js_set definiert eine neue NGINX-Variable, $access_log_headers , und die JavaScript-Funktion, die sie füllt. Die Direktive log_format definiert ein neues Format namens kvpairs , das jede Protokollzeile mit dem Wert von $access_log_headers schreibt.

Der Serverblock definiert einen einfachen HTTP-Reverse-Proxy, der alle Anfragen an https://www.example.com weiterleitet. Die Direktive „access_log“ gibt an, dass alle Anfragen im kvpairs -Format protokolliert werden.

Schauen wir uns nun den JavaScript-Code an, der einen Protokolleintrag vorbereitet.

 

Der Rückgabewert der Funktion kvAccess – ein Protokolleintrag – wird an die Konfigurationsdirektive js_set in rawheader_logging.conf übergeben. Bedenken Sie, dass NGINX-Variablen bei Bedarf ausgewertet werden und dies wiederum bedeutet, dass die durch js_set definierte JavaScript-Funktion ausgeführt wird, wenn der Wert der Variablen benötigt wird. In diesem Beispiel wird $access_log_headers in der log_format -Direktive verwendet und daher wird kvAccess() zum Zeitpunkt der Protokollierung ausgeführt. Variablen, die im Rahmen von Map- oder Rewrite- Direktiven verwendet werden (in diesem Beispiel nicht dargestellt), lösen die entsprechende JavaScript-Ausführung in einer früheren Verarbeitungsphase aus.

Wir können diese erweiterte Protokollierungslösung von NGINX JavaScript in Aktion sehen, indem wir eine Anforderung durch unseren Reverse-Proxy leiten und den resultierenden Protokolldateieintrag beobachten, der Anforderungsheader mit dem Präfix „in. “ und Antwortheader mit dem Präfix „ out. “ enthält.

$ curl http://127.0.0.1/ $ tail --lines=1 /var/log/nginx/access_headers.log 2021-04-23T10:08:15+00:00 client=172.17.0.1 method=GET uri=/index.html status=200 in.Host=localhost:55081 in.User-Agent=curl/7.64.1 in.Accept=*/* out.Content-Type=text/html out.Content-Length=612 out.ETag=\x22606339ef-264\x22 out.Accept-Ranges=bytes

Ein Großteil der Nützlichkeit von NGINX JavaScript beruht auf seinem Zugriff auf die internen Komponenten von NGINX. Dieses Beispiel nutzt mehrere Eigenschaften des request( r )-Objekts . Das Stream NGINX JavaScript-Modul (für TCP- und UDP-Anwendungen) verwendet ein oder mehrere Sitzungsobjekte mit einem eigenen Satz von Eigenschaften. Weitere Beispiele für NGINX-JavaScript-Lösungen für HTTP und TCP/UDP finden Sie unter Anwendungsfälle für das NGINX-JavaScript-Modul .

Wir würden gerne von den Anwendungsfällen hören, die Sie sich für NGINX JavaScript ausdenken – erzählen Sie uns davon bitte im Kommentarbereich unten.


Anwendungsfälle für das NGINX JavaScript-Modul

Sehen Sie sich diese Blogbeiträge an, um weitere HTTP- und TCP/UDP-Anwendungsfälle für das NGINX JavaScript-Modul kennenzulernen:

 


[ngx_snippet name=’njs-enable-instructions’]


„Dieser Blogbeitrag kann auf Produkte verweisen, die nicht mehr verfügbar und/oder nicht mehr unterstützt werden. Die aktuellsten Informationen zu verfügbaren F5 NGINX-Produkten und -Lösungen finden Sie in unserer NGINX-Produktfamilie . NGINX ist jetzt Teil von F5. Alle vorherigen NGINX.com-Links werden auf ähnliche NGINX-Inhalte auf F5.com umgeleitet."