OpenResty's Killer-Funktion: Dynamik

API7.ai

January 12, 2023

OpenResty (NGINX + Lua)

Bisher sind wir fast mit den leistungsbezogenen Inhalten von OpenResty fertig. Das Beherrschen und flexible Anwenden dieser Optimierungstechniken kann die Leistung unseres Codes erheblich verbessern. Heute, im letzten Teil der Leistungsoptimierung, lernen wir eine oft unterschätzte Fähigkeit von OpenResty kennen: "dynamisch".

Beginnen wir damit, zu verstehen, was dynamisch bedeutet und wie es mit der Leistung zusammenhängt. Dynamisch bedeutet in diesem Kontext, dass Programme Parameter, Konfigurationen und sogar ihren Code zur Laufzeit ändern können, ohne neu geladen werden zu müssen. Konkret bedeutet dies in NGINX und OpenResty, dass Sie Upstreams, SSL-Zertifikate und Ratenbegrenzungsschwellen ändern können, ohne den Dienst neu starten zu müssen, um Dynamik zu erreichen. Was die Beziehung zwischen Dynamik und Leistung betrifft, ist es offensichtlich, dass, wenn diese Arten von Operationen nicht dynamisch durchgeführt werden können, häufige Neustarts der NGINX-Dienste natürlich zu Leistungseinbußen führen.

Wir wissen jedoch, dass die Open-Source-Version von NGINX keine dynamischen Funktionen unterstützt, sodass Sie Upstream-SSL-Zertifikate ändern müssen, indem Sie die Konfigurationsdatei ändern und den Dienst neu starten, um sie wirksam zu machen. NGINX Plus (die kommerzielle Version von NGINX) bietet einige dynamische Funktionen, und Sie können die REST-API verwenden, um Updates durchzuführen, aber dies ist bestenfalls eine weniger radikale Verbesserung.

In OpenResty existieren diese Fesseln nicht, und Dynamik ist die Killerfunktion von OpenResty. Sie fragen sich vielleicht, warum OpenResty, das auf NGINX basiert, Dynamik unterstützen kann. Der Grund ist einfach: Die NGINX-Logik wird über C-Module realisiert, während OpenResty über Lua, eine Skriptsprache, realisiert wird. Einer der Vorteile von Skriptsprachen ist, dass sie zur Laufzeit dynamisch geändert werden können.

Code dynamisch laden

Hier ist, wie man Lua-Code in OpenResty dynamisch lädt.

resty -e 'local s = [[ngx.say("hello world")]]
local func, err = loadstring(s)
func()'

Wir können sehen, dass wir mit nur wenigen Codezeilen eine Zeichenkette in eine Lua-Funktion verwandeln und sie ausführen können. Schauen wir uns diese Codezeilen genauer an:

  • Zuerst deklarieren wir eine Zeichenkette, deren Inhalt ein Stück Lua-Code ist, das hello world ausgibt;
  • Dann verwenden wir die loadstring-Funktion in Lua, um das Zeichenkettenobjekt in das Funktionsobjekt func zu verwandeln.
  • Schließlich fügen wir dem Funktionsnamen Klammern hinzu, um die func auszuführen und hello world auszugeben.

Natürlich können wir auch interessantere und praktischere Funktionen auf der Grundlage dieses Codes erweitern. Als Nächstes werde ich Sie dazu einladen, es auszuprobieren.

Funktion 1: FaaS

Zuerst kommt FaaS (Function-as-a-Service), das in letzter Zeit eine sehr beliebte Technologierichtung war. Sehen wir uns an, wie man es in OpenResty implementiert. Im gerade erwähnten Code ist die Zeichenkette ein Lua-Code. Wir können sie auch in eine Lua-Funktion ändern:

local s = [[
 return function()
     ngx.say("hello world")
end
]]

Wie gesagt, sind Funktionen erstklassige Bürger in Lua, und dieser Code gibt eine anonyme Funktion zurück. Wenn wir diese anonyme Funktion ausführen, verwenden wir pcall, um eine Schutzschicht bereitzustellen. pcall führt die Funktion im geschützten Modus aus und fängt die Ausnahme ab. Wenn alles normal ist, gibt es true und das Ausführungsergebnis zurück. Wenn es fehlschlägt, gibt es false und Fehlerinformationen zurück, was der folgende Code ist:

local func1, err = loadstring(s)
local ret, func = pcall(func1)

Natürlich, wenn Sie die beiden oben genannten Teile kombinieren, erhalten Sie ein vollständiges und ausführbares Beispiel:

resty -e 'local s = [[
 return function()
    ngx.say("hello world")
end
]]
local  func1 = loadstring(s)
local ret, func = pcall(func1)
func()'

Um einen Schritt weiter zu gehen, können wir die Zeichenkette s, die Funktionen enthält, in eine Form ändern, die Benutzer angeben können, und die Bedingungen für ihre Ausführung hinzufügen. Dies ist der Prototyp von FaaS. Hier biete ich eine vollständige Implementierung. Wenn Sie an FaaS interessiert sind und Ihre Forschung fortsetzen möchten, gehen Sie über den Link, um mehr zu erfahren.

Funktion 2: Edge Computing

Die Dynamik von OpenResty kann für FaaS verwendet werden, wodurch die Dynamik der Skriptsprache auf die Funktionsebene verfeinert wird und eine dynamische Rolle im Edge Computing spielt.

Aufgrund dieser Vorteile können wir die Fühler von OpenResty von den Bereichen API-Gateway, WAF (Web Application Firewall), Webserver und anderen Serverenden zu den Benutzern am nächsten gelegenen Edge-Knoten wie IoT-Geräte, CDN-Edge-Knoten, Router usw. ausdehnen.

Dies ist nicht nur eine Fantasie. OpenResty wird in den oben genannten Bereichen bereits weit verbreitet eingesetzt. Nehmen wir als Beispiel CDN-Edge-Knoten: Cloudflare, der größte Benutzer von OpenResty, hat die dynamische Steuerung von CDN-Edge-Knoten mit Hilfe der dynamischen Eigenschaften von OpenResty schon lange realisiert.

Der Ansatz von Cloudflare ähnelt dem oben genannten Prinzip des dynamischen Ladens von Code und kann grob in die folgenden Schritte unterteilt werden:

  • Zuerst werden die geänderten Codedateien aus dem Key-Value-Datenbankcluster abgerufen. Die Methode kann Hintergrund-Timer-Polling oder "Publish-Subscribe"-Modus zur Überwachung sein;
  • Dann wird die alte Datei auf der lokalen Festplatte durch die aktualisierte Codedatei ersetzt und der im Speicher geladene Cache mit den Methoden loadstring und pcall aktualisiert;

Auf diese Weise wird die nächste Client-Anfrage, die verarbeitet wird, die aktualisierte Codelogik durchlaufen. Natürlich sollten in der praktischen Anwendung mehr Details als die oben genannten Schritte berücksichtigt werden, wie z.B. Versionskontrolle und Rollback, Ausnahmebehandlung, Netzwerkunterbrechung, Neustart von Edge-Knoten usw., aber der Gesamtprozess bleibt unverändert.

Wenn wir den Ansatz von Cloudflare von CDN-Edge-Knoten auf andere Edge-Szenarien übertragen, können wir viel Rechenleistung dynamisch an Edge-Knotengeräte zuweisen. Dies kann nicht nur die Rechenleistung der Edge-Knoten voll ausschöpfen, sondern auch den Benutzern schnellere Antworten auf Anfragen ermöglichen, da der Edge-Knoten die ursprünglichen Daten verarbeitet und sie dann an den Remote-Server zusammenfasst, was die Menge der zu übertragenden Daten erheblich reduziert.

Um jedoch eine hervorragende Arbeit in FaaS und Edge Computing zu leisten, ist die Dynamik von OpenResty nur eine gute Grundlage. Sie müssen auch die Verbesserung Ihres Umgebungsökosystems und die Beteiligung von Herstellern berücksichtigen, was nicht nur eine technische Kategorie ist.

Dynamischer Upstream

Lassen Sie uns nun unsere Gedanken zurück zu OpenResty ziehen, um zu sehen, wie man dynamischen Upstream erreicht. lua-resty-core bietet eine Bibliothek von ngx.balancer, um den Upstream einzurichten. Sie muss in der balancer-Phase von OpenResty platziert werden, um ausgeführt zu werden:

balancer_by_lua_block {
    local balancer = require "ngx.balancer"
    local host = "127.0.0.2"
    local port = 8080

    local ok, err = balancer.set_current_peer(host, port)
    if not ok then
        ngx.log(ngx.ERR, "failed to set the current peer: ", err)
        return ngx.exit(500)
    end
}

Die Funktion set_current_peer richtet die Upstream-IP-Adresse und den Port ein. Wir möchten jedoch darauf hinweisen, dass hier der Domainname nicht unterstützt wird. Wir müssen die Bibliothek lua-resty-dns verwenden, um eine Schicht der Analyse für den Domainnamen und die IP durchzuführen.

Allerdings ist ngx.balancer relativ niedrig. Obwohl es verwendet werden kann, um den Upstream einzurichten, ist die Realisierung des dynamischen Upstreams weit davon entfernt, einfach zu sein. Daher werden zwei Funktionen vor ngx.balancer benötigt:

  • Zuerst muss entschieden werden, ob der Upstream-Auswahlalgorithmus consistent hash oder roundrobin ist;
  • Zweitens der Upstream-Gesundheitscheck-Mechanismus, der ungesunde Upstreams ausschließen und sie wieder hinzufügen muss, wenn ungesunde Upstreams gesund werden.

Die Bibliothek von OpenResty's offiziellem lua-resty-balancer enthält zwei Arten von Algorithmen: resty.chash und resty.roundrobin, um die erste Funktion zu erfüllen, und es hat lua-resty-upstream-healthcheck, um die zweite Funktion zu versuchen.

Es gibt jedoch noch zwei Probleme.

Der erste Punkt ist das Fehlen einer vollständigen Implementierung der letzten Meile. ngx.balancer, lua-resty-balancer und lua-resty-upstream-healthcheck zu kombinieren, um die Funktionen des dynamischen Upstreams zu erfüllen, erfordert noch einige Arbeit, was die meisten Entwickler aufhält.

Zweitens ist die Implementierung von lua-resty-upstream-healthcheck nicht vollständig. Es gibt nur passive Gesundheitschecks, aber keine aktiven Gesundheitschecks.

Die Anfragen der Clients lösen hier den passiven Gesundheitscheck aus und analysieren dann den Rückgabewert des Upstreams als Bedingung, um festzustellen, ob die Gesundheit gut ist. Ob der Upstream gesund ist, ist unbekannt, wenn es keine Client-Anfragen gibt. Aktive Gesundheitschecks können diesen Mangel beheben. Es verwendet ngx.timer, um periodisch die angegebene Upstream-Schnittstelle abzufragen, um den Gesundheitsstatus zu überprüfen.

Daher empfehlen wir in der Praxis normalerweise die Verwendung von lua-resty-healthcheck, um Upstream-Gesundheitschecks durchzuführen. Sein Vorteil ist, dass es sowohl aktive als auch passive Gesundheitschecks enthält und in mehreren Projekten mit höherer Zuverlässigkeit verifiziert wurde.

Darüber hinaus hat das aufstrebende Microservice-API-Gateway Apache APISIX eine vollständige Implementierung des dynamischen Upstreams auf der Grundlage von lua-resty-upstream-healthcheck durchgeführt. Wir können seine Implementierung als Referenz verwenden. Es gibt insgesamt nur 400 Codezeilen. Sie können es leicht abziehen und in Ihr Projekt einfügen, um es zu verwenden.

Zusammenfassung

In Bezug auf die Dynamik von OpenResty, in welchen Bereichen und Szenarien können Sie sie nutzen? Sie können auch die Inhalte jedes in diesem Kapitel vorgestellten Teils für eine detailliertere und tiefere Analyse erweitern.

Sie sind eingeladen, diesen Artikel zu teilen und mit mehr Menschen zu lernen und Fortschritte zu machen.