Drei häufig verwendete Lua Resty-Bibliotheken in OpenResty

API7.ai

January 13, 2023

OpenResty (NGINX + Lua)

Das Erlernen von Programmiersprachen und Plattformen ist oft eine Frage des Verständnisses der Standard- und Drittanbieter-Bibliotheken und nicht der Syntax selbst. Nachdem wir die API und Leistungsoptimierungstechniken gelernt haben, müssen wir die Verwendung verschiedener lua-resty-Bibliotheken erlernen, um unsere OpenResty-Fähigkeiten auf mehr Szenarien auszudehnen.

Wo findet man die lua-resty-Bibliothek?

Im Vergleich zu PHP, Python und JavaScript sind die aktuellen Standard- und Drittanbieter-Bibliotheken von OpenResty noch relativ spärlich, und es ist nicht einfach, die richtigen lua-resty-Bibliotheken zu finden. Hier sind jedoch zwei empfohlene Quellen, die Ihnen helfen, sie schneller zu finden.

Die erste Empfehlung ist das von Aapo gepflegte Repository awesome-resty. Dieses Repository organisiert OpenResty-bezogene Bibliotheken nach Kategorien und ist allumfassend, einschließlich NGINX-C-Module, lua-resty-Bibliotheken, Web-Frameworks, Routing-Bibliotheken, Templates, Test-Frameworks usw. Es ist Ihre erste Wahl für OpenResty-Ressourcen.

Wenn Sie in Aapos Repository nicht die richtige Bibliothek finden, können Sie auch bei luarocks, topm oder GitHub nachschauen. Es könnte einige Bibliotheken geben, die noch nicht lange Open Source sind und nicht viel Aufmerksamkeit erhalten haben.

In den vorherigen Artikeln haben wir bereits einige nützliche Bibliotheken wie lua-resty-mlcache, lua-resty-traffic, lua-resty-shell usw. kennengelernt. Heute, im letzten Artikel des OpenResty-Leistungsoptimierungsabschnitts, lernen wir drei weitere einzigartige periphere Bibliotheken kennen, die alle von Entwicklern in der Community beigetragen wurden.

Leistungsverbesserung von ngx.var

Zuerst schauen wir uns ein C-Modul an: lua-var-nginx-module. Wie ich bereits erwähnt habe, ist ngx.var eine relativ leistungsintensive Operation. Daher müssen wir in der Praxis ngx.ctx als eine Ebene des Caches verwenden.

Gibt es also eine Möglichkeit, das Leistungsproblem von ngx.var vollständig zu lösen?

Dieses C-Modul hat in dieser Hinsicht einige Experimente durchgeführt, und die Ergebnisse sind bemerkenswert, mit einer 5-fachen Leistungssteigerung gegenüber ngx.var. Es verwendet den FFI-Ansatz, daher müssen Sie OpenResty zuerst mit der folgenden Kompilierungsoption kompilieren.

./configure --prefix=/opt/openresty \
         --add-module=/path/to/lua-var-nginx-module

Dann installieren Sie die Lua-Bibliothek mit luarocks auf folgende Weise:

luarocks install lua-resty-ngxvar

Die hier aufgerufene Methode ist ebenfalls sehr einfach und erfordert nur eine Zeile der fetch-Funktion. Sie funktioniert genauso wie das ursprüngliche ngx.var.remote_addr, um die IP-Adresse des Clients zu erhalten.

content_by_lua_block {
    local var = require("resty.ngxvar")
    ngx.say(var.fetch("remote_addr"))
}

Nachdem Sie diese grundlegenden Operationen verstanden haben, sind Sie vielleicht neugieriger darauf, wie dieses Modul eine signifikante Leistungssteigerung erreicht. Wie wir immer sagen: "Vor dem Quellcode gibt es keine Geheimnisse". Lassen Sie uns also herausfinden, wie die remote_addr-Variable abgerufen wird.

ngx_int_t
ngx_http_lua_var_ffi_remote_addr(ngx_http_request_t *r, ngx_str_t *remote_addr)
{
    remote_addr->len = r->connection->addr_text.len;
    remote_addr->data = r->connection->addr_text.data;

    return NGX_OK;
}

Nachdem Sie diesen Code gelesen haben, werden Sie sehen, dass dieser Lua-FFI-Ansatz dem Ansatz von lua-resty-core entspricht. Es hat den offensichtlichen Vorteil, dass FFI verwendet wird, um Variablen direkt zu erhalten und die ursprüngliche Suchlogik von ngx.var zu umgehen. Sein Nachteil ist offensichtlich: Das Hinzufügen von C-Funktionen und FFI-Aufrufen für jede Variable, die Sie erhalten möchten, ist zeit- und energieaufwendig.

Einige Leute mögen fragen: "Warum sage ich, dass dies zeit- und energieaufwendig ist? Sieht der obige C-Code nicht ziemlich substanziell aus?" Schauen wir uns die Quelle dieser Codezeilen an, die aus src/http/ngx_http_variables.c im NGINX-Code stammen.

static ngx_int_t
ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
    v->len = r->connection->addr_text.len;
    v->valid = 1;
    v->no_cacheable = 0;
    v->not_found = 0;
    v->data = r->connection->addr_text.data;

    return NGX_OK;
}

Nachdem Sie den Quellcode gesehen haben, ist das Geheimnis gelüftet! lua-var-nginx-module ist ein Portier des NGINX-Variablencodes, mit FFI-Wrapping in der äußeren Schicht, und auf diese Weise erreicht es Leistungsoptimierung. Dies ist eine gute Idee und eine gute Richtung für die Optimierung.

Wenn Sie eine Bibliothek oder ein Werkzeug lernen, dürfen Sie nicht nur auf der Ebene der Operation stehen bleiben, sondern müssen auch fragen, warum wir es tun, und den Quellcode betrachten. Natürlich ermutige ich Sie auch stark, Code beizutragen, um mehr NGINX-Variablen zu unterstützen.

JSON Schema

Hier stelle ich eine lua-resty-Bibliothek vor: lua-rapidjson. Es ist ein Wrapper um rapidjson, die Open-Source-JSON-Bibliothek von Tencent, und ist für ihre Leistung bekannt. Hier konzentrieren wir uns auf den Unterschied zwischen ihr und cjson: JSON Schema-Unterstützung.

JSON Schema ist ein gängiger Standard, der es uns ermöglicht, das Format der Parameter in einer Schnittstelle genau zu beschreiben und wie sie validiert werden sollen. Hier ist ein einfaches Beispiel:

"stringArray": {
    "type": "array",
    "items": { "type": "string" },
    "minItems": 1,
    "uniqueItems": true
}

Dieses JSON beschreibt genau, dass der stringArray-Parameter ein String-Array ist und dass das Array nicht leer sein darf, noch können die Array-Elemente dupliziert werden.

lua-rapidjson ermöglicht es uns, JSON Schema in OpenResty zu verwenden, was der Schnittstellenvalidierung große Vorteile bringen kann. Zum Beispiel können wir für die zuvor beschriebene Limit-Count-Schnittstelle das folgende Schema verwenden:

local schema = {
    type = "object",
    properties = {
        count = {type = "integer", minimum = 0},
        time_window = {type = "integer",  minimum = 0},
        key = {type = "string", enum = {"remote_addr", "server_addr"}},
        rejected_code = {type = "integer", minimum = 200, maximum = 600},
    },
    additionalProperties = false,
    required = {"count", "time_window", "key", "rejected_code"},
}

Sie werden feststellen, dass dies zwei sehr offensichtliche Vorteile hat:

  1. Für das Frontend kann das Frontend diese Schema-Beschreibung direkt für die Frontend-Seitenentwicklung und Parametervalidierung wiederverwenden, ohne sich um das Backend kümmern zu müssen.
  2. Für das Backend verwendet das Backend direkt die Schema-Validierungsfunktion SchemaValidator von lua-rapidjson, um die Legitimität der Schnittstelle zu bestimmen, und es ist keine zusätzliche Code-Erstellung erforderlich.

Worker-Kommunikation

Schließlich möchte ich über die lua-resty-Bibliothek sprechen, die die Kommunikation zwischen Workern in OpenResty ermöglicht, wo es keinen Mechanismus für die direkte Kommunikation zwischen Workern gibt, was viele Probleme verursacht. Stellen wir uns ein Szenario vor:

Ein OpenResty-Dienst hat 24 Worker-Prozesse, und wenn der Administrator eine Konfiguration des Systems über die REST-HTTP-APIs aktualisiert, erhält nur ein Worker die Aktualisierung vom Administrator und schreibt das Ergebnis in die Datenbank, aktualisiert das shared dict und den lru cache innerhalb seines eigenen Workers. Wie können also die anderen 23 Worker benachrichtigt werden, diese Konfiguration zu aktualisieren?

Ein Benachrichtigungsmechanismus zwischen mehreren Workers ist erforderlich, um die obige Aufgabe zu erfüllen. In dem Fall, dass OpenResty dies nicht unterstützt, müssen wir mit shared dict-Daten über Worker hinweg retten.

lua-resty-worker-events ist eine konkrete Implementierung dieser Idee. Es verwaltet eine Versionsnummer in einem shared dict, und wenn eine neue Nachricht veröffentlicht wird, wird die Versionsnummer um eins erhöht und der Inhalt der Nachricht mit der Versionsnummer als key in das Wörterbuch gelegt.

event_id, err = _dict:incr(KEY_LAST_ID, 1)
success, err = _dict:add(KEY_DATA .. tostring(event_id), json)

Außerdem wird im Hintergrund eine polling-Schleife mit einem Standardintervall von 1 Sekunde mit ngx.timer erstellt, um ständig nach Änderungen in der Versionsnummer zu suchen:

local event_id, err = get_event_id()
if event_id == _last_event then
    return "done"
end

Auf diese Weise wird, sobald eine neue Ereignisbenachrichtigung zur Verarbeitung gefunden wird, der Nachrichteninhalt basierend auf der Versionsnummer aus dem shared dict abgerufen:

while _last_event < event_id do
    count = count + 1
    _last_event = _last_event + 1
    data, err = _dict:get(KEY_DATA..tostring(_last_event))
end

Insgesamt hat lua-resty-worker-events zwar eine Verzögerung von einer Sekunde, implementiert aber dennoch einen Worker-zu-Worker-Ereignisbenachrichtigungsmechanismus.

In einigen Echtzeit-Szenarien, wie z.B. beim Nachrichten-Pushing, kann das Fehlen einer direkten Kommunikation zwischen Worker-Prozessen in OpenResty jedoch einige Probleme verursachen. Es gibt keine bessere Lösung dafür, aber wenn Sie gute Ideen haben, diskutieren Sie diese bitte auf Github. Viele Funktionen von OpenResty werden von der Community angetrieben, um einen virtuellen ökologischen Kreislauf aufzubauen.

Zusammenfassung

Die drei Bibliotheken, die wir heute vorgestellt haben, sind einzigartig und eröffnen mehr Möglichkeiten für OpenResty-Anwendungen. Zum Schluss ein interaktives Thema: Haben Sie interessante Bibliotheken rund um OpenResty gefunden? Oder was finden oder fragen Sie sich über die heute erwähnten Bibliotheken? Sie sind herzlich eingeladen, diesen Artikel an die OpenResty-Benutzer in Ihrer Umgebung zu senden, um gemeinsam auszutauschen und Fortschritte zu machen.