Zum Inhalt

redis2: NGINX Upstream-Modul für das Redis 2.0-Protokoll

Installation

Sie können dieses Modul in jeder RHEL-basierten Distribution installieren, einschließlich, aber nicht beschränkt auf:

  • RedHat Enterprise Linux 7, 8, 9 und 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 und Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-redis2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install nginx-module-redis2

Aktivieren Sie das Modul, indem Sie Folgendes an den Anfang von /etc/nginx/nginx.conf hinzufügen:

load_module modules/ngx_http_redis2_module.so;

Dieses Dokument beschreibt nginx-module-redis2 v0.15, veröffentlicht am 19. April 2018.


 location = /foo {
     set $value 'first';
     redis2_query set one $value;
     redis2_pass 127.0.0.1:6379;
 }

 # GET /get?key=some_key
 location = /get {
     set_unescape_uri $key $arg_key;  # dies erfordert ngx_set_misc
     redis2_query get $key;
     redis2_pass foo.com:6379;
 }

 # GET /set?key=one&val=first%20value
 location = /set {
     set_unescape_uri $key $arg_key;  # dies erfordert ngx_set_misc
     set_unescape_uri $val $arg_val;  # dies erfordert ngx_set_misc
     redis2_query set $key $val;
     redis2_pass foo.com:6379;
 }

 # mehrere pipelined Abfragen
 location = /foo {
     set $value 'first';
     redis2_query set one $value;
     redis2_query get one;
     redis2_query set one two;
     redis2_query get one;
     redis2_pass 127.0.0.1:6379;
 }

 location = /bar {
     # $ ist hier nicht speziell...
     redis2_literal_raw_query '*1\r\n$4\r\nping\r\n';
     redis2_pass 127.0.0.1:6379;
 }

 location = /bar {
     # Variablen können unten verwendet werden und $ ist speziell
     redis2_raw_query 'get one\r\n';
     redis2_pass 127.0.0.1:6379;
 }

 # GET /baz?get%20foo%0d%0a
 location = /baz {
     set_unescape_uri $query $query_string; # dies erfordert das ngx_set_misc-Modul
     redis2_raw_query $query;
     redis2_pass 127.0.0.1:6379;
 }

 location = /init {
     redis2_query del key1;
     redis2_query lpush key1 C;
     redis2_query lpush key1 B;
     redis2_query lpush key1 A;
     redis2_pass 127.0.0.1:6379;
 }

 location = /get {
     redis2_query lrange key1 0 -1;
     redis2_pass 127.0.0.1:6379;
 }

Beschreibung

Dies ist ein Nginx Upstream-Modul, das NGINX ermöglicht, mit einem Redis 2.x-Server auf nicht blockierende Weise zu kommunizieren. Das vollständige Redis 2.0 einheitliche Protokoll wurde implementiert, einschließlich der Unterstützung für Redis-Pipelining.

Dieses Modul gibt die rohe TCP-Antwort vom Redis-Server zurück. Es wird empfohlen, meinen lua-redis-parser (geschrieben in reinem C) zu verwenden, um diese Antworten in Lua-Datenstrukturen zu parsen, wenn es mit dem lua-nginx-module kombiniert wird.

Wenn es zusammen mit dem lua-nginx-module verwendet wird, wird jedoch empfohlen, die lua-resty-redis Bibliothek anstelle dieses Moduls zu verwenden, da erstere viel flexibler und speichereffizienter ist.

Wenn Sie nur den get Redis-Befehl verwenden möchten, können Sie das HttpRedisModule ausprobieren. Es gibt nur den geparsten Inhaltsbereich der Redis-Antwort zurück, da nur get implementiert werden muss.

Eine weitere Möglichkeit besteht darin, die Redis-Antworten selbst auf der Client-Seite zu parsen.

Direktiven

redis2_query

syntax: redis2_query cmd arg1 arg2 ...

default: nein

context: location, location if

Geben Sie einen Redis-Befehl an, indem Sie seine einzelnen Argumente (einschließlich des Redis-Befehlsnamens selbst) ähnlich wie beim redis-cli-Dienstprogramm angeben.

Mehrere Instanzen dieser Direktive sind in einer einzelnen Location erlaubt, und diese Abfragen werden gepipelined. Zum Beispiel,

 location = /pipelined {
     redis2_query set hello world;
     redis2_query get hello;

     redis2_pass 127.0.0.1:$TEST_NGINX_REDIS_PORT;
 }

Dann ergibt GET /pipelined zwei aufeinanderfolgende rohe Redis-Antworten

 +OK
 $5
 world

wobei Zeilenumbrüche hier tatsächlich CR LF (\r\n) sind.

redis2_raw_query

syntax: redis2_raw_query QUERY

default: nein

context: location, location if

Geben Sie rohe Redis-Abfragen an, und NGINX-Variablen werden im QUERY-Argument erkannt.

Nur ein Redis-Befehl ist im QUERY-Argument erlaubt, andernfalls erhalten Sie einen Fehler. Wenn Sie mehrere pipelined Befehle in einer einzigen Abfrage angeben möchten, verwenden Sie stattdessen die redis2_raw_queries Direktive.

redis2_raw_queries

syntax: redis2_raw_queries N QUERIES

default: nein

context: location, location if

Geben Sie N Befehle im QUERIES-Argument an. Sowohl die N- als auch die QUERIES-Argumente können NGINX-Variablen annehmen.

Hier einige Beispiele

 location = /pipelined {
     redis2_raw_queries 3 "flushall\r\nget key1\r\nget key2\r\n";
     redis2_pass 127.0.0.1:6379;
 }

 # GET /pipelined2?n=2&cmds=flushall%0D%0Aget%20key%0D%0A
 location = /pipelined2 {
     set_unescape_uri $n $arg_n;
     set_unescape_uri $cmds $arg_cmds;

     redis2_raw_queries $n $cmds;

     redis2_pass 127.0.0.1:6379;
 }
Beachten Sie, dass in dem obigen zweiten Beispiel die set_unescape_uri Direktive vom set-misc-nginx-module bereitgestellt wird.

redis2_literal_raw_query

syntax: redis2_literal_raw_query QUERY

default: nein

context: location, location if

Geben Sie eine rohe Redis-Abfrage an, aber NGINX-Variablen darin werden nicht erkannt. Mit anderen Worten, Sie können das Dollarzeichen-Zeichen ($) in Ihrem QUERY-Argument frei verwenden.

Nur ein Redis-Befehl ist im QUERY-Argument erlaubt.

redis2_pass

syntax: redis2_pass <upstream_name>

syntax: redis2_pass <host>:<port>

default: nein

context: location, location if

phase: content

Geben Sie den Redis-Server-Backend an.

redis2_connect_timeout

syntax: redis2_connect_timeout <time>

default: 60s

context: http, server, location

Die Zeitüberschreitung für die Verbindung zum Redis-Server, standardmäßig in Sekunden.

Es ist ratsam, immer die Zeiteinheit ausdrücklich anzugeben, um Verwirrung zu vermeiden. Unterstützte Zeiteinheiten sind s(Sekunden), ms(Millisekunden), y(Jahre), M(Monate), w(Wochen), d(Tage), h(Stunden) und m(Minuten).

Diese Zeit muss weniger als 597 Stunden betragen.

redis2_send_timeout

syntax: redis2_send_timeout <time>

default: 60s

context: http, server, location

Die Zeitüberschreitung für das Senden von TCP-Anfragen an den Redis-Server, standardmäßig in Sekunden.

Es ist ratsam, immer die Zeiteinheit ausdrücklich anzugeben, um Verwirrung zu vermeiden. Unterstützte Zeiteinheiten sind s(Sekunden), ms(Millisekunden), y(Jahre), M(Monate), w(Wochen), d(Tage), h(Stunden) und m(Minuten).

redis2_read_timeout

syntax: redis2_read_timeout <time>

default: 60s

context: http, server, location

Die Zeitüberschreitung für das Lesen von TCP-Antworten vom Redis-Server, standardmäßig in Sekunden.

Es ist ratsam, immer die Zeiteinheit ausdrücklich anzugeben, um Verwirrung zu vermeiden. Unterstützte Zeiteinheiten sind s(Sekunden), ms(Millisekunden), y(Jahre), M(Monate), w(Wochen), d(Tage), h(Stunden) und m(Minuten).

redis2_buffer_size

syntax: redis2_buffer_size <size>

default: 4k/8k

context: http, server, location

Diese Puffergröße wird zum Lesen von Redis-Antworten verwendet, muss jedoch nicht so groß sein wie die größte mögliche Redis-Antwort.

Diese Standardgröße ist die Seitengröße und kann 4k oder 8k betragen.

redis2_next_upstream

syntax: redis2_next_upstream [ error | timeout | invalid_response | off ]

default: error timeout

context: http, server, location

Geben Sie an, welche Fehlerbedingungen dazu führen sollen, dass die Anfrage an einen anderen Upstream-Server weitergeleitet wird. Gilt nur, wenn der Wert in redis2_pass ein Upstream mit zwei oder mehr Servern ist.

Hier ein künstliches Beispiel:

 upstream redis_cluster {
     server 127.0.0.1:6379;
     server 127.0.0.1:6380;
 }

 server {
     location = /redis {
         redis2_next_upstream error timeout invalid_response;
         redis2_query get foo;
         redis2_pass redis_cluster;
     }
 }

Verbindungspool

Sie können das hervorragende HttpUpstreamKeepaliveModule mit diesem Modul verwenden, um einen TCP-Verbindungspool für Redis bereitzustellen.

Ein Beispiel für einen Konfigurationsausschnitt sieht so aus:

 http {
     upstream backend {
       server 127.0.0.1:6379;

       # ein Pool mit maximal 1024 Verbindungen
       # und keine Unterscheidung der Server:
       keepalive 1024;
     }

     server {
         ...
         location = /redis {
             set_unescape_uri $query $arg_query;
             redis2_query $query;
             redis2_pass backend;
         }
     }
 }

Auswahl von Redis-Datenbanken

Redis bietet den select Befehl, um Redis-Datenbanken zu wechseln. Dieser Befehl unterscheidet sich nicht von anderen normalen Befehlen wie get oder set. Sie können sie also in redis2_query Direktiven verwenden, zum Beispiel,

redis2_query select 8;
redis2_query get foo;

Lua-Interoperabilität

Dieses Modul kann als nicht blockierender redis2-Client für das lua-nginx-module dienen (aber heutzutage wird empfohlen, die lua-resty-redis Bibliothek zu verwenden, die viel einfacher zu verwenden und in den meisten Fällen effizienter ist). Hier ist ein Beispiel, das eine GET-Subanfrage verwendet:

 location = /redis {
     internal;

     # set_unescape_uri wird von ngx_set_misc bereitgestellt
     set_unescape_uri $query $arg_query;

     redis2_raw_query $query;
     redis2_pass 127.0.0.1:6379;
 }

 location = /main {
     content_by_lua '
         local res = ngx.location.capture("/redis",
             { args = { query = "ping\\r\\n" } }
         )
         ngx.print("[" .. res.body .. "]")
     ';
 }

Der Zugriff auf /main ergibt

[+PONG\r\n]

wobei \r\n CRLF ist. Das heißt, dieses Modul gibt die rohen TCP-Antworten vom entfernten Redis-Server zurück. Für Lua-basierte Anwendungsentwickler möchten sie möglicherweise die lua-redis-parser Bibliothek (geschrieben in reinem C) verwenden, um solche rohen Antworten in Lua-Datenstrukturen zu parsen.

Wenn Sie den inline Lua-Code in eine externe .lua-Datei verschieben, ist es wichtig, die Escape-Sequenz \r\n direkt zu verwenden. Wir haben oben \\r\\n verwendet, nur weil der Lua-Code selbst beim Einfügen in einen NGINX-String-Literal zitiert werden muss.

Sie können auch POST/PUT-Subanfragen verwenden, um die rohe Redis-Anfrage über den Anfragekörper zu übertragen, was keine URI-Escaping und -Unescaping erfordert und somit einige CPU-Zyklen spart. Hier ist ein solches Beispiel:

 location = /redis {
     internal;

     # $echo_request_body wird vom ngx_echo-Modul bereitgestellt
     redis2_raw_query $echo_request_body;

     redis2_pass 127.0.0.1:6379;
 }

 location = /main {
     content_by_lua '
         local res = ngx.location.capture("/redis",
             { method = ngx.HTTP_PUT,
               body = "ping\\r\\n" }
         )
         ngx.print("[" .. res.body .. "]")
     ';
 }

Dies ergibt genau die gleiche Ausgabe wie das vorherige (GET) Beispiel.

Man kann auch Lua verwenden, um einen konkreten Redis-Backend basierend auf einigen komplizierten Hashing-Regeln auszuwählen. Zum Beispiel,

 upstream redis-a {
     server foo.bar.com:6379;
 }

 upstream redis-b {
     server bar.baz.com:6379;
 }

 upstream redis-c {
     server blah.blah.org:6379;
 }

 server {
     ...

     location = /redis {
         set_unescape_uri $query $arg_query;
         redis2_query $query;
         redis2_pass $arg_backend;
     }

     location = /foo {
         content_by_lua "
             -- wähle zufällig einen Server aus
             local servers = {'redis-a', 'redis-b', 'redis-c'}
             local i = ngx.time() % #servers + 1;
             local srv = servers[i]

             local res = ngx.location.capture('/redis',
                 { args = {
                     query = '...',
                     backend = srv
                   }
                 }
             )
             ngx.say(res.body)
         ";
     }
 }

Pipelined Redis-Anfragen mit Lua

Hier ist ein vollständiges Beispiel, das zeigt, wie man Lua verwendet, um mehrere pipelined Redis-Anfragen über dieses NGINX-Modul auszugeben.

Zunächst fügen wir Folgendes in unsere nginx.conf-Datei ein:

 location = /redis2 {
     internal;

     redis2_raw_queries $args $echo_request_body;
     redis2_pass 127.0.0.1:6379;
 }

 location = /test {
     content_by_lua_file conf/test.lua;
 }

Im Grunde verwenden wir URI-Abfrageargumente, um die Anzahl der Redis-Anfragen und den Anfragekörper zu übergeben, um die pipelined Redis-Anfragezeichenfolge zu übergeben.

Dann erstellen wir die Datei conf/test.lua (deren Pfad relativ zum Serverstamm von NGINX ist), um den folgenden Lua-Code zu enthalten:

 -- conf/test.lua
 local parser = require "redis.parser"

 local reqs = {
     {"set", "foo", "hello world"},
     {"get", "foo"}
 }

 local raw_reqs = {}
 for i, req in ipairs(reqs) do
     table.insert(raw_reqs, parser.build_query(req))
 end

 local res = ngx.location.capture("/redis2?" .. #reqs,
     { body = table.concat(raw_reqs, "") })

 if res.status ~= 200 or not res.body then
     ngx.log(ngx.ERR, "Fehler bei der Abfrage von Redis")
     ngx.exit(500)
 end

 local replies = parser.parse_replies(res.body, #reqs)
 for i, reply in ipairs(replies) do
     ngx.say(reply[1])
 end

Hier gehen wir davon aus, dass Ihr Redis-Server auf dem Standardport (6379) des localhost lauscht. Wir verwenden auch die lua-redis-parser Bibliothek, um rohe Redis-Abfragen für uns zu erstellen und verwenden sie auch, um die Antworten zu parsen.

Der Zugriff auf die /test-Location über HTTP-Clients wie curl ergibt die folgende Ausgabe

OK
hello world

Eine realistischere Einstellung ist es, eine ordnungsgemäße Upstream-Definition für unser Redis-Backend zu verwenden und den TCP-Verbindungspool über die keepalive Direktive darin zu aktivieren.

Redis Publish/Subscribe Unterstützung

Dieses Modul hat eine begrenzte Unterstützung für die Redis Publish/Subscribe-Funktion. Es kann aufgrund der zustandslosen Natur des REST- und HTTP-Modells nicht vollständig unterstützt werden.

Betrachten Sie folgendes Beispiel:

 location = /redis {
     redis2_raw_queries 2 "subscribe /foo/bar\r\n";
     redis2_pass 127.0.0.1:6379;
 }

Und dann veröffentlichen Sie eine Nachricht für den Schlüssel /foo/bar in der redis-cli-Befehlszeile. Dann erhalten Sie zwei Multi-Bulk-Antworten von der /redis-Location.

Sie können die Antworten sicherlich mit der lua-redis-parser Bibliothek parsen, wenn Sie Lua verwenden, um auf die Location dieses Moduls zuzugreifen.

Einschränkungen für Redis Publish/Subscribe

Wenn Sie die Redis pub/sub Funktion mit diesem Modul verwenden möchten, sollten Sie die folgenden Einschränkungen beachten:

  • Sie können das HttpUpstreamKeepaliveModule nicht mit diesem Redis-Upstream verwenden. Nur kurze Redis-Verbindungen funktionieren.
  • Es kann einige Wettlaufbedingungen geben, die die harmlosen Redis server returned extra bytes Warnungen in Ihrem nginx's error.log erzeugen. Solche Warnungen könnten selten sein, aber seien Sie darauf vorbereitet.
  • Sie sollten die verschiedenen Zeitüberschreitungs-Einstellungen, die von diesem Modul bereitgestellt werden, wie redis2_connect_timeout und redis2_read_timeout, anpassen.

Wenn Sie mit diesen Einschränkungen nicht leben können, wird dringend empfohlen, zur lua-resty-redis Bibliothek für das lua-nginx-module zu wechseln.

Leistungstuning

  • Wenn Sie dieses Modul verwenden, stellen Sie bitte sicher, dass Sie einen TCP-Verbindungspool (bereitgestellt durch das HttpUpstreamKeepaliveModule) und Redis-Pipelining, wo immer möglich, verwenden. Diese Funktionen verbessern die Leistung erheblich.
  • Die Verwendung mehrerer Instanzen von Redis-Servern auf Ihren Mehrkernmaschinen hilft ebenfalls sehr aufgrund der sequentiellen Verarbeitungsnatur einer einzelnen Redis-Serverinstanz.
  • Wenn Sie die Leistung mit etwas wie ab oder http_load benchmarken, stellen Sie bitte sicher, dass Ihr Fehlerprotokollniveau hoch genug ist (wie warn), um zu verhindern, dass NGINX-Arbeiter zu viele Zyklen mit dem Leeren der error.log-Datei verbringen, die immer nicht gepuffert und blockierend ist und somit sehr teuer ist.

SEE ALSO

GitHub

Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-redis2.