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;
}
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 bytesWarnungen 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
aboderhttp_loadbenchmarken, stellen Sie bitte sicher, dass Ihr Fehlerprotokollniveau hoch genug ist (wiewarn), um zu verhindern, dass NGINX-Arbeiter zu viele Zyklen mit dem Leeren dererror.log-Datei verbringen, die immer nicht gepuffert und blockierend ist und somit sehr teuer ist.
SEE ALSO
- Die Redis Server-Homepage.
- Das Redis-Datenprotokoll: http://redis.io/topics/protocol
- Ein Redis-Antwortparser und ein Anfragekonstruktor für Lua: lua-redis-parser.
- lua-nginx-module
- Das ngx_openresty-Bundle.
- Die lua-resty-redis Bibliothek, die auf der lua-nginx-module cosocket API basiert.
GitHub
Sie finden möglicherweise zusätzliche Konfigurationstipps und Dokumentationen für dieses Modul im GitHub-Repository für nginx-module-redis2.