Aller au contenu

redis: pilote client Lua redis pour nginx-module-lua basé sur l'API cosocket

Installation

Si vous n'avez pas configuré l'abonnement au dépôt RPM, inscrivez-vous. Ensuite, vous pouvez procéder avec les étapes suivantes.

CentOS/RHEL 7 ou Amazon Linux 2

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 lua-resty-redis

CentOS/RHEL 8+, Fedora Linux, Amazon Linux 2023

dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-redis

Pour utiliser cette bibliothèque Lua avec NGINX, assurez-vous que nginx-module-lua est installé.

Ce document décrit lua-resty-redis v0.33 publié le 09 juillet 2025.


Cette bibliothèque Lua est un pilote client Redis pour le module ngx_lua de nginx :

https://github.com/openresty/lua-nginx-module/#readme

Cette bibliothèque Lua tire parti de l'API cosocket de ngx_lua, qui garantit un comportement 100 % non-bloquant.

Notez qu'au moins ngx_lua 0.5.14 ou OpenResty 1.2.1.14 est requis.

Synopsis

    # vous n'avez pas besoin de la ligne suivante si vous utilisez
    # le bundle OpenResty :
    server {
        location /test {
            # besoin de spécifier le résolveur pour résoudre le nom d'hôte
            resolver 8.8.8.8;

            content_by_lua_block {
                local redis = require "resty.redis"
                local red = redis:new()

                red:set_timeouts(1000, 1000, 1000) -- 1 sec

                -- ou se connecter à un fichier de socket de domaine unix écouté
                -- par un serveur redis :
                --     local ok, err = red:connect("unix:/path/to/redis.sock")

                -- se connecter directement via l'adresse IP
                local ok, err = red:connect("127.0.0.1", 6379)

                -- ou se connecter via le nom d'hôte, besoin de spécifier le résolveur comme ci-dessus
                local ok, err = red:connect("redis.openresty.com", 6379)

                if not ok then
                    ngx.say("échec de la connexion : ", err)
                    return
                end

                ok, err = red:set("dog", "un animal")
                if not ok then
                    ngx.say("échec de la définition de dog : ", err)
                    return
                end

                ngx.say("résultat de la définition : ", ok)

                local res, err = red:get("dog")
                if not res then
                    ngx.say("échec de l'obtention de dog : ", err)
                    return
                end

                if res == ngx.null then
                    ngx.say("dog non trouvé.")
                    return
                end

                ngx.say("dog : ", res)

                red:init_pipeline()
                red:set("cat", "Marry")
                red:set("horse", "Bob")
                red:get("cat")
                red:get("horse")
                local results, err = red:commit_pipeline()
                if not results then
                    ngx.say("échec de l'engagement des requêtes en pipeline : ", err)
                    return
                end

                for i, res in ipairs(results) do
                    if type(res) == "table" then
                        if res[1] == false then
                            ngx.say("échec de l'exécution de la commande ", i, ": ", res[2])
                        else
                            -- traiter la valeur du tableau
                        end
                    else
                        -- traiter la valeur scalaire
                    end
                end

                -- le mettre dans le pool de connexions de taille 100,
                -- avec un temps d'inactivité maximum de 10 secondes
                local ok, err = red:set_keepalive(10000, 100)
                if not ok then
                    ngx.say("échec de la définition du keepalive : ", err)
                    return
                end

                -- ou simplement fermer la connexion immédiatement :
                -- local ok, err = red:close()
                -- if not ok then
                --     ngx.say("échec de la fermeture : ", err)
                --     return
                -- end
            }
        }
    }

Méthodes

Toutes les commandes Redis ont leurs propres méthodes avec le même nom, sauf qu'elles sont toutes en minuscules.

Vous pouvez trouver la liste complète des commandes Redis ici :

http://redis.io/commands

Vous devez consulter cette référence de commande Redis pour voir quelles commandes Redis acceptent quels arguments.

Les arguments de commande Redis peuvent être directement fournis dans l'appel de méthode correspondant. Par exemple, la commande redis "GET" accepte un seul argument clé, vous pouvez donc simplement appeler la méthode "get" comme ceci :

    local res, err = red:get("key")

De même, la commande redis "LRANGE" accepte trois arguments, vous devez donc appeler la méthode "lrange" comme ceci :

    local res, err = red:lrange("nokey", 0, 1)

Par exemple, les commandes "SET", "GET", "LRANGE" et "BLPOP" correspondent aux méthodes "set", "get", "lrange" et "blpop".

Voici quelques exemples supplémentaires :

    -- HMGET myhash field1 field2 nofield
    local res, err = red:hmget("myhash", "field1", "field2", "nofield")
    -- HMSET myhash field1 "Hello" field2 "World"
    local res, err = red:hmset("myhash", "field1", "Hello", "field2", "World")

Toutes ces méthodes de commande retournent un seul résultat en cas de succès et nil sinon. En cas d'erreurs ou d'échecs, elle renverra également une seconde valeur qui est une chaîne décrivant l'erreur.

Une "réponse de statut" Redis résulte en une valeur de retour de type chaîne avec le préfixe "+" supprimé.

Une "réponse entière" Redis résulte en une valeur de retour de type nombre Lua.

Une "réponse d'erreur" Redis résulte en une valeur false et une chaîne décrivant l'erreur.

Une réponse "bulk" Redis non nulle résulte en une chaîne Lua comme valeur de retour. Une réponse bulk nulle résulte en une valeur de retour ngx.null.

Une réponse "multi-bulk" Redis non nulle résulte en une table Lua contenant toutes les valeurs composantes (le cas échéant). Si l'une des valeurs composantes est une valeur d'erreur redis valide, alors ce sera une table à deux éléments {false, err}.

Une réponse multi-bulk nulle retourne une valeur ngx.null.

Voir http://redis.io/topics/protocol pour des détails concernant les différents types de réponses Redis.

En plus de toutes ces méthodes de commande redis, les méthodes suivantes sont également fournies :

new

syntax: red, err = redis:new()

Crée un objet redis. En cas d'échec, retourne nil et une chaîne décrivant l'erreur.

connect

syntax: ok, err = red:connect(host, port, options_table?)

syntax: ok, err = red:connect("unix:/path/to/unix.sock", options_table?)

Tente de se connecter à l'hôte distant et au port que le serveur redis écoute ou à un fichier de socket de domaine unix local écouté par le serveur redis.

Avant de résoudre effectivement le nom d'hôte et de se connecter au backend distant, cette méthode cherchera toujours dans le pool de connexions des connexions inactives correspondantes créées par des appels précédents de cette méthode.

L'argument optionnel options_table est une table Lua contenant les clés suivantes :

  • ssl

    S'il est défini sur true, utilise SSL pour se connecter à redis (par défaut false).

  • ssl_verify

    S'il est défini sur true, vérifie la validité du certificat SSL du serveur (par défaut false). Notez que vous devez configurer lua_ssl_trusted_certificate pour spécifier le certificat CA (ou serveur) utilisé par votre serveur redis. Vous devrez peut-être également configurer lua_ssl_verify_depth en conséquence.

  • server_name

    Spécifie le nom du serveur pour la nouvelle extension TLS Server Name Indication (SNI) lors de la connexion via SSL.

  • pool

    Spécifie un nom personnalisé pour le pool de connexions utilisé. S'il est omis, le nom du pool de connexions sera généré à partir du modèle de chaîne <host>:<port> ou <unix-socket-path>.

  • pool_size

    Spécifie la taille du pool de connexions. S'il est omis et qu'aucune option backlog n'a été fournie, aucun pool ne sera créé. S'il est omis mais que backlog a été fourni, le pool sera créé avec une taille par défaut égale à la valeur de la directive lua_socket_pool_size. Le pool de connexions contient jusqu'à pool_size connexions vivantes prêtes à être réutilisées par des appels ultérieurs à connect, mais notez qu'il n'y a pas de limite supérieure au nombre total de connexions ouvertes en dehors du pool. Si vous devez restreindre le nombre total de connexions ouvertes, spécifiez l'option backlog. Lorsque le pool de connexions dépasserait sa limite de taille, la connexion la moins récemment utilisée (keep-alive) déjà dans le pool sera fermée pour faire de la place pour la connexion actuelle. Notez que le pool de connexions cosocket est par processus de travail Nginx plutôt que par instance de serveur Nginx, donc la limite de taille spécifiée ici s'applique également à chaque processus de travail Nginx. Notez également que la taille du pool de connexions ne peut pas être modifiée une fois qu'il a été créé. Notez qu'au moins ngx_lua 0.10.14 est requis pour utiliser ces options.

  • backlog

    Si spécifié, ce module limitera le nombre total de connexions ouvertes pour ce pool. Pas plus de connexions que pool_size peuvent être ouvertes pour ce pool à tout moment. Si le pool de connexions est plein, les opérations de connexion suivantes seront mises en file d'attente dans une file d'attente égale à la valeur de cette option (la file d'attente "backlog"). Si le nombre d'opérations de connexion mises en file d'attente est égal à backlog, les opérations de connexion suivantes échoueront et retourneront nil plus la chaîne d'erreur "trop d'opérations de connexion en attente". Les opérations de connexion mises en file d'attente seront reprises une fois que le nombre de connexions dans le pool est inférieur à pool_size. L'opération de connexion mise en file d'attente sera abandonnée une fois qu'elle a été mise en file d'attente pendant plus de connect_timeout, contrôlé par set_timeout, et retournera nil plus la chaîne d'erreur "timeout". Notez qu'au moins ngx_lua 0.10.14 est requis pour utiliser ces options.

set_timeout

syntax: red:set_timeout(time)

Définit la protection de délai d'attente (en ms) pour les opérations suivantes, y compris la méthode connect.

Depuis la version v0.28 de ce module, il est conseillé d'utiliser set_timeouts plutôt que cette méthode.

set_timeouts

syntax: red:set_timeouts(connect_timeout, send_timeout, read_timeout)

Définit respectivement les seuils de délai d'attente de connexion, d'envoi et de lecture (en ms) pour les opérations de socket suivantes. Définir les seuils de délai d'attente avec cette méthode offre plus de granularité que set_timeout. En tant que tel, il est préférable d'utiliser set_timeouts plutôt que set_timeout.

Cette méthode a été ajoutée dans la version v0.28.

set_keepalive

syntax: ok, err = red:set_keepalive(max_idle_timeout, pool_size)

Met immédiatement la connexion Redis actuelle dans le pool de connexions cosocket ngx_lua.

Vous pouvez spécifier le délai d'inactivité maximum (en ms) lorsque la connexion est dans le pool et la taille maximale du pool pour chaque processus de travail nginx.

En cas de succès, retourne 1. En cas d'erreurs, retourne nil avec une chaîne décrivant l'erreur.

N'appelez cette méthode que là où vous auriez appelé la méthode close à la place. Appeler cette méthode transformera immédiatement l'objet redis actuel en état closed. Toute opération ultérieure autre que connect() sur l'objet actuel retournera l'erreur closed.

get_reused_times

syntax: times, err = red:get_reused_times()

Cette méthode retourne le nombre de fois (avec succès) que la connexion actuelle a été réutilisée. En cas d'erreur, elle retourne nil et une chaîne décrivant l'erreur.

Si la connexion actuelle ne provient pas du pool de connexions intégré, alors cette méthode retourne toujours 0, c'est-à-dire que la connexion n'a jamais été réutilisée (encore). Si la connexion provient du pool de connexions, alors la valeur de retour est toujours non nulle. Ainsi, cette méthode peut également être utilisée pour déterminer si la connexion actuelle provient du pool.

close

syntax: ok, err = red:close()

Ferme la connexion redis actuelle et retourne le statut.

En cas de succès, retourne 1. En cas d'erreurs, retourne nil avec une chaîne décrivant l'erreur.

init_pipeline

syntax: red:init_pipeline()

syntax: red:init_pipeline(n)

Active le mode de pipeline redis. Tous les appels suivants aux méthodes de commande Redis seront automatiquement mis en cache et envoyés au serveur en une seule fois lorsque la méthode commit_pipeline est appelée ou annulés en appelant la méthode cancel_pipeline.

Cette méthode réussit toujours.

Si l'objet redis est déjà en mode pipeline Redis, alors appeler cette méthode annulera les requêtes Redis mises en cache existantes.

L'argument optionnel n spécifie le nombre (approximatif) de commandes qui vont être ajoutées à ce pipeline, ce qui peut rendre les choses un peu plus rapides.

commit_pipeline

syntax: results, err = red:commit_pipeline()

Quitte le mode pipeline en engageant toutes les requêtes Redis mises en cache au serveur distant en une seule fois. Toutes les réponses à ces requêtes seront collectées automatiquement et seront retournées comme s'il s'agissait d'une grande réponse multi-bulk au plus haut niveau.

Cette méthode retourne nil et une chaîne Lua décrivant l'erreur en cas d'échecs.

cancel_pipeline

syntax: red:cancel_pipeline()

Quitte le mode pipeline en annulant toutes les commandes Redis mises en cache existantes depuis le dernier appel à la méthode init_pipeline.

Cette méthode réussit toujours.

Si l'objet redis n'est pas en mode pipeline Redis, alors cette méthode est une opération no-op.

hmset

syntax: res, err = red:hmset(myhash, field1, value1, field2, value2, ...)

syntax: res, err = red:hmset(myhash, { field1 = value1, field2 = value2, ... })

Wrapper spécial pour la commande Redis "hmset".

Lorsqu'il n'y a que trois arguments (y compris l'objet "red" lui-même), alors le dernier argument doit être une table Lua contenant toutes les paires champ/valeur.

array_to_hash

syntax: hash = red:array_to_hash(array)

Fonction auxiliaire qui convertit une table Lua de type tableau en une table de type hash.

Cette méthode a été introduite pour la première fois dans la version v0.11.

read_reply

syntax: res, err = red:read_reply()

Lit une réponse du serveur redis. Cette méthode est principalement utile pour l'API Pub/Sub de Redis, par exemple,

    local cjson = require "cjson"
    local redis = require "resty.redis"

    local red = redis:new()
    local red2 = redis:new()

    red:set_timeouts(1000, 1000, 1000) -- 1 sec
    red2:set_timeouts(1000, 1000, 1000) -- 1 sec

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("1: échec de la connexion : ", err)
        return
    end

    ok, err = red2:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("2: échec de la connexion : ", err)
        return
    end

    local res, err = red:subscribe("dog")
    if not res then
        ngx.say("1: échec de l'abonnement : ", err)
        return
    end

    ngx.say("1: abonnement : ", cjson.encode(res))

    res, err = red2:publish("dog", "Hello")
    if not res then
        ngx.say("2: échec de la publication : ", err)
        return
    end

    ngx.say("2: publication : ", cjson.encode(res))

    res, err = red:read_reply()
    if not res then
        ngx.say("1: échec de la lecture de la réponse : ", err)
        return
    end

    ngx.say("1: réception : ", cjson.encode(res))

    red:close()
    red2:close()

L'exécution de cet exemple donne la sortie suivante :

1: abonnement : ["subscribe","dog",1]
2: publication : 1
1: réception : ["message","dog","Hello"]

Les méthodes de classe suivantes sont fournies :

add_commands

syntax: hash = redis.add_commands(cmd_name1, cmd_name2, ...)

AVERTISSEMENT cette méthode est maintenant obsolète puisque nous générons déjà automatiquement des méthodes Lua pour toutes les commandes redis que l'utilisateur tente d'utiliser et nous n'en avons donc plus besoin.

Ajoute de nouvelles commandes redis à la classe resty.redis. Voici un exemple :

    local redis = require "resty.redis"

    redis.add_commands("foo", "bar")

    local red = redis:new()

    red:set_timeouts(1000, 1000, 1000) -- 1 sec

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("échec de la connexion : ", err)
        return
    end

    local res, err = red:foo("a")
    if not res then
        ngx.say("échec de foo : ", err)
    end

    res, err = red:bar()
    if not res then
        ngx.say("échec de bar : ", err)
    end

Authentification Redis

Redis utilise la commande AUTH pour faire de l'authentification : http://redis.io/commands/auth

Il n'y a rien de spécial pour cette commande par rapport à d'autres commandes Redis comme GET et SET. Vous pouvez donc simplement invoquer la méthode auth sur votre instance resty.redis. Voici un exemple :

    local redis = require "resty.redis"
    local red = redis:new()

    red:set_timeouts(1000, 1000, 1000) -- 1 sec

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("échec de la connexion : ", err)
        return
    end

    local res, err = red:auth("foobared")
    if not res then
        ngx.say("échec de l'authentification : ", err)
        return
    end

où nous supposons que le serveur Redis est configuré avec le mot de passe foobared dans le fichier redis.conf :

requirepass foobared

Si le mot de passe spécifié est incorrect, alors l'exemple ci-dessus affichera ce qui suit au client HTTP :

échec de l'authentification : ERR mot de passe invalide

Transactions Redis

Cette bibliothèque prend en charge les transactions Redis. Voici un exemple :

    local cjson = require "cjson"
    local redis = require "resty.redis"
    local red = redis:new()

    red:set_timeouts(1000, 1000, 1000) -- 1 sec

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("échec de la connexion : ", err)
        return
    end

    local ok, err = red:multi()
    if not ok then
        ngx.say("échec de l'exécution de multi : ", err)
        return
    end
    ngx.say("réponse multi : ", cjson.encode(ok))

    local ans, err = red:set("a", "abc")
    if not ans then
        ngx.say("échec de l'exécution de sort : ", err)
        return
    end
    ngx.say("réponse set : ", cjson.encode(ans))

    local ans, err = red:lpop("a")
    if not ans then
        ngx.say("échec de l'exécution de sort : ", err)
        return
    end
    ngx.say("réponse set : ", cjson.encode(ans))

    ans, err = red:exec()
    ngx.say("réponse exec : ", cjson.encode(ans))

    red:close()

Ensuite, la sortie sera

réponse multi : "OK"
réponse set : "QUEUED"
réponse set : "QUEUED"
réponse exec : ["OK",[false,"ERR Opération contre une clé contenant le mauvais type de valeur"]]

Module Redis

Cette bibliothèque prend en charge le module Redis. Voici un exemple avec le module RedisBloom :

    local cjson = require "cjson"
    local redis = require "resty.redis"
    -- enregistrer le préfixe de module "bf" pour RedisBloom
    redis.register_module_prefix("bf")

    local red = redis:new()

    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.say("échec de la connexion : ", err)
        return
    end

    -- appeler la commande BF.ADD avec le préfixe 'bf'
    res, err = red:bf():add("dog", 1)
    if not res then
        ngx.say(err)
        return
    end
    ngx.say("réception : ", cjson.encode(res))

    -- appeler la commande BF.EXISTS
    res, err = red:bf():exists("dog")
    if not res then
        ngx.say(err)
        return
    end
    ngx.say("réception : ", cjson.encode(res))

Équilibrage de charge et basculement

Vous pouvez facilement implémenter votre propre logique d'équilibrage de charge Redis vous-même en Lua. Il suffit de garder une table Lua de toutes les informations de backend Redis disponibles (comme le nom d'hôte et les numéros de port) et de choisir un serveur selon une règle (comme le round-robin ou le hachage basé sur la clé) à partir de la table Lua à chaque requête. Vous pouvez suivre l'état de la règle actuelle dans les données de votre propre module Lua, voir https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker

De même, vous pouvez implémenter une logique de basculement automatique en Lua avec une grande flexibilité.

Débogage

Il est généralement pratique d'utiliser la bibliothèque lua-cjson pour encoder les valeurs de retour des méthodes de commande redis en JSON. Par exemple,

    local cjson = require "cjson"
    ...
    local res, err = red:mget("h1234", "h5678")
    if res then
        print("res : ", cjson.encode(res))
    end

Journalisation automatique des erreurs

Par défaut, le module sous-jacent ngx_lua effectue une journalisation des erreurs lorsque des erreurs de socket se produisent. Si vous gérez déjà correctement les erreurs dans votre propre code Lua, il est recommandé de désactiver cette journalisation automatique des erreurs en désactivant la directive lua_socket_log_errors de ngx_lua, c'est-à-dire,

    lua_socket_log_errors off;

Liste de contrôle pour les problèmes

  1. Assurez-vous de configurer correctement la taille du pool de connexions dans set_keepalive. En gros, si votre Redis peut gérer n connexions concurrentes et que votre NGINX a m travailleurs, alors la taille du pool de connexions doit être configurée comme n/m. Par exemple, si votre Redis gère généralement 1000 requêtes concurrentes et que vous avez 10 travailleurs NGINX, alors la taille du pool de connexions doit être de 100. De même, si vous avez p instances NGINX différentes, alors la taille du pool de connexions doit être n/m/p.
  2. Assurez-vous que le paramètre de backlog côté Redis est suffisamment grand. Pour Redis 2.8+, vous pouvez directement ajuster le paramètre tcp-backlog dans le fichier redis.conf (et également ajuster le paramètre du noyau SOMAXCONN en conséquence, au moins sous Linux). Vous voudrez peut-être également ajuster le paramètre maxclients dans redis.conf.
  3. Assurez-vous de ne pas utiliser un paramètre de délai d'attente trop court dans les méthodes set_timeout ou set_timeouts. Si vous devez le faire, essayez de refaire l'opération en cas de délai d'attente et de désactiver la journalisation automatique des erreurs (car vous gérez déjà correctement les erreurs dans votre propre code Lua).
  4. Si l'utilisation du CPU de vos processus de travail NGINX est très élevée sous charge, alors la boucle d'événements NGINX pourrait être bloquée par le calcul CPU trop longtemps. Essayez d'échantillonner un C-land on-CPU Flame Graph et un Lua-land on-CPU Flame Graph pour un processus de travail NGINX typique. Vous pouvez optimiser les choses liées au CPU en fonction de ces Flame Graphs.
  5. Si l'utilisation du CPU de vos processus de travail NGINX est très faible sous charge, alors la boucle d'événements NGINX pourrait être bloquée par certains appels système bloquants (comme les appels système d'E/S de fichiers). Vous pouvez confirmer le problème en exécutant l'outil epoll-loop-blocking-distr contre un processus de travail NGINX typique. Si c'est effectivement le cas, alors vous pouvez échantillonner un C-land off-CPU Flame Graph pour un processus de travail NGINX afin d'analyser les véritables blocages.
  6. Si votre processus redis-server fonctionne près de 100 % d'utilisation CPU, alors vous devriez envisager de mettre à l'échelle votre backend Redis par plusieurs nœuds ou d'utiliser l'outil C-land on-CPU Flame Graph pour analyser les goulets d'étranglement internes au sein du processus serveur Redis.

Limitations

  • Cette bibliothèque ne peut pas être utilisée dans des contextes de code comme init_by_lua, set_by_lua, log_by_lua, et header_filter_by_lua où l'API cosocket ngx_lua n'est pas disponible.
  • L'instance d'objet resty.redis ne peut pas être stockée dans une variable Lua au niveau du module Lua, car elle sera alors partagée par toutes les requêtes concurrentes traitées par le même processus de travail nginx (voir https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker) et entraînera de mauvaises conditions de concurrence lorsque des requêtes concurrentes essaient d'utiliser la même instance resty.redis (vous verrez l'erreur "mauvaise requête" ou "socket occupé" retournée par les appels de méthodes). Vous devez toujours initier des objets resty.redis dans des variables locales de fonction ou dans la table ngx.ctx. Ces endroits ont tous leurs propres copies de données pour chaque requête.

Cloner la dernière version, en supposant v0.29

wget https://github.com/openresty/lua-resty-redis/archive/refs/tags/v0.29.tar.gz

Extraire

tar -xvzf v0.29.tar.gz

aller dans le répertoire

cd lua-resty-redis-0.29

export LUA_LIB_DIR=/usr/local/openresty/site/lualib

Compiler et installer

make install

Maintenant, le chemin compilé sera affiché

/usr/local/lib/lua/resty = lua_package_path dans la conf nginx

```

Voir aussi

GitHub

Vous pouvez trouver des conseils de configuration supplémentaires et de la documentation pour ce module dans le dépôt GitHub pour nginx-module-redis.