Aller au contenu
# *lua*: Support de script Lua pour NGINX

## Installation

Vous pouvez installer ce module dans n'importe quelle distribution basée sur RHEL, y compris, mais sans s'y limiter :

* RedHat Enterprise Linux 7, 8, 9 et 10
* CentOS 7, 8, 9
* AlmaLinux 8, 9
* Rocky Linux 8, 9
* Amazon Linux 2 et Amazon Linux 2023

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

    ```bash
    dnf -y install https://extras.getpagespeed.com/release-latest.rpm
    dnf -y install nginx-module-lua
    ```

=== "CentOS/RHEL 7 et Amazon Linux 2"

    ```bash
    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-lua
    ```

Activez le module en ajoutant ce qui suit en haut de `/etc/nginx/nginx.conf` :

```nginx
load_module modules/ngx_http_lua_module.so;

Ce document décrit nginx-module-lua v0.10.31 publié le 29 mai 2026.


Vous êtes invités à vous abonner à notre chaîne YouTube officielle, OpenResty.

Synopsis

 # définir les chemins de recherche pour les bibliothèques externes Lua pures (';;' est le chemin par défaut) :

 # définir les chemins de recherche pour les bibliothèques externes Lua écrites en C (peut également utiliser ';;') :

 server {
     location /lua_content {
         # Type MIME déterminé par default_type :
         default_type 'text/plain';

         content_by_lua_block {
             ngx.say('Hello, world!')
         }
     }

     location /nginx_var {
         # Type MIME déterminé par default_type :
         default_type 'text/plain';

         # essayer d'accéder à /nginx_var?a=hello,world
         content_by_lua_block {
             ngx.say(ngx.var.arg_a)
         }
     }

     location = /request_body {
         client_max_body_size 50k;
         client_body_buffer_size 50k;

         content_by_lua_block {
             ngx.req.read_body()  -- lire explicitement le corps de la requête
             local data = ngx.req.get_body_data()
             if data then
                 ngx.say("données du corps :")
                 ngx.print(data)
                 return
             end

             -- le corps peut être mis en mémoire tampon dans un fichier temporaire :
             local file = ngx.req.get_body_file()
             if file then
                 ngx.say("le corps est dans le fichier ", file)
             else
                 ngx.say("aucun corps trouvé")
             end
         }
     }

     # I/O non-bloquant transparent en Lua via des sous-requêtes
     # (enfin, une meilleure façon est d'utiliser des cosockets)
     location = /lua {
         # Type MIME déterminé par default_type :
         default_type 'text/plain';

         content_by_lua_block {
             local res = ngx.location.capture("/some_other_location")
             if res then
                 ngx.say("statut : ", res.status)
                 ngx.say("corps :")
                 ngx.print(res.body)
             end
         }
     }

     location = /foo {
         rewrite_by_lua_block {
             res = ngx.location.capture("/memc",
                 { args = { cmd = "incr", key = ngx.var.uri } }
             )
         }

         proxy_pass http://blah.blah.com;
     }

     location = /mixed {
         rewrite_by_lua_file /path/to/rewrite.lua;
         access_by_lua_file /path/to/access.lua;
         content_by_lua_file /path/to/content.lua;
     }

     # utiliser la variable nginx dans le chemin du code
     # ATTENTION : le contenu dans la variable nginx doit être soigneusement filtré,
     # sinon il y aura un grand risque de sécurité !
     location ~ ^/app/([-_a-zA-Z0-9/]+) {
         set $path $1;
         content_by_lua_file /path/to/lua/app/root/$path.lua;
     }

     location / {
        client_max_body_size 100k;
        client_body_buffer_size 100k;

        access_by_lua_block {
            -- vérifier que l'adresse IP du client est dans notre liste noire
            if ngx.var.remote_addr == "132.5.72.3" then
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end

            -- vérifier si l'URI contient des mots mauvais
            if ngx.var.uri and
                   string.match(ngx.var.request_body, "evil")
            then
                return ngx.redirect("/terms_of_use.html")
            end

            -- tests réussis
        }

        # paramètres proxy_pass/fastcgi_pass/etc.
     }
 }

Description

Ce module intègre LuaJIT 2.0/2.1 dans Nginx. C'est un composant central de OpenResty. Si vous utilisez ce module, alors vous utilisez essentiellement OpenResty.

Depuis la version v0.10.16 de ce module, l'interpréteur Lua standard (connu sous le nom de "PUC-Rio Lua") n'est plus supporté. Ce document utilise de manière interchangeable les termes "Lua" et "LuaJIT" pour désigner l'interpréteur LuaJIT.

En tirant parti des sous-requêtes de Nginx, ce module permet l'intégration des puissants threads Lua (appelés "coroutines" Lua) dans le modèle d'événements de Nginx.

Contrairement à mod_lua d'Apache et mod_magnet de Lighttpd, le code Lua exécuté à l'aide de ce module peut être 100 % non-bloquant sur le trafic réseau tant que l' API Nginx pour Lua fournie par ce module est utilisée pour gérer les requêtes vers des services en amont tels que MySQL, PostgreSQL, Memcached, Redis, ou des services web HTTP en amont.

Au moins les bibliothèques Lua et modules Nginx suivants peuvent être utilisés avec ce module :

Presque tous les modules Nginx peuvent être utilisés avec ce module ngx_lua par le biais de ngx.location.capture ou ngx.location.capture_multi, mais il est recommandé d'utiliser ces bibliothèques lua-resty-* au lieu de créer des sous-requêtes pour accéder aux modules Nginx en amont car les premières sont généralement beaucoup plus flexibles et efficaces en mémoire.

L'interpréteur Lua (également connu sous le nom de "État Lua" ou "instance de VM Lua") est partagé entre toutes les requêtes dans un seul processus de travail Nginx pour minimiser l'utilisation de la mémoire. Les contextes de requête sont séparés à l'aide de coroutines Lua légères.

Les modules Lua chargés persistent au niveau du processus de travail Nginx, ce qui entraîne une petite empreinte mémoire en Lua même sous des charges lourdes.

Ce module est intégré dans le sous-système "http" de Nginx, il ne peut donc parler que des protocoles de communication en aval dans la famille HTTP (HTTP 0.9/1.0/1.1/2.0, WebSockets, etc...). Si vous souhaitez effectuer des communications TCP génériques avec les clients en aval, vous devez utiliser le ngx_stream_lua module à la place, qui offre une API Lua compatible.

Utilisations typiques

Pour en nommer quelques-unes :

  • Fusionner et traiter les sorties de divers modules en amont Nginx (proxy, drizzle, postgres, redis, memcached, etc.) en Lua,
  • effectuer des contrôles d'accès et de sécurité arbitrairement complexes en Lua avant que les requêtes n'atteignent réellement les backends en amont,
  • manipuler les en-têtes de réponse de manière arbitraire (via Lua)
  • récupérer des informations de backend à partir de backends de stockage externes (comme redis, memcached, mysql, postgresql) et utiliser ces informations pour choisir quel backend en amont accéder à la volée,
  • coder des applications web arbitrairement complexes dans un gestionnaire de contenu en utilisant un accès synchrone mais toujours non-bloquant aux backends de base de données et autres stockages,
  • effectuer un dispatch d'URL très complexe en Lua lors de la phase de réécriture,
  • utiliser Lua pour implémenter un mécanisme de mise en cache avancé pour les sous-requêtes Nginx et des emplacements arbitraires.

Les possibilités sont illimitées car le module permet de rassembler divers éléments au sein de Nginx tout en exposant la puissance du langage Lua à l'utilisateur. Le module fournit la pleine flexibilité du scripting tout en offrant des niveaux de performance comparables à ceux des programmes en langage C natif, tant en termes de temps CPU que d'empreinte mémoire grâce à LuaJIT 2.x.

D'autres implémentations de langages de script ont généralement du mal à égaler ce niveau de performance.

Compatibilité Nginx

La dernière version de ce module est compatible avec les versions suivantes de Nginx :

  • 1.29.x (dernier test : 1.29.8)
  • 1.29.x (dernier test : 1.29.2)
  • 1.27.x (dernier test : 1.27.1)
  • 1.25.x (dernier test : 1.25.1)
  • 1.21.x (dernier test : 1.21.4)
  • 1.19.x (dernier test : 1.19.3)
  • 1.17.x (dernier test : 1.17.8)
  • 1.15.x (dernier test : 1.15.8)
  • 1.14.x
  • 1.13.x (dernier test : 1.13.6)
  • 1.12.x
  • 1.11.x (dernier test : 1.11.2)
  • 1.10.x
  • 1.9.x (dernier test : 1.9.15)
  • 1.8.x
  • 1.7.x (dernier test : 1.7.10)
  • 1.6.x

Les cœurs Nginx plus anciens que 1.6.0 (exclusif) ne sont pas pris en charge.

Dépôt de code

Le dépôt de code de ce projet est hébergé sur GitHub à openresty/lua-nginx-module.

Support de bytecode LuaJIT

Regardez la vidéo YouTube "Measure Execution Time of Lua Code Correctly in OpenResty"

Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup

À partir de la version v0.5.0rc32, toutes les directives de configuration *_by_lua_file (comme content_by_lua_file) prennent en charge le chargement direct de fichiers de bytecode brut LuaJIT 2.0/2.1 :

 /path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc

L'option -bg peut être utilisée pour inclure des informations de débogage dans le fichier de bytecode LuaJIT :

 /path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc

Veuillez vous référer à la documentation officielle de LuaJIT sur l'option -b pour plus de détails :

https://luajit.org/running.html#opt_b

Notez que les fichiers de bytecode générés par LuaJIT 2.1 ne sont pas compatibles avec LuaJIT 2.0, et vice versa. Le support pour le bytecode LuaJIT 2.1 a été ajouté pour la première fois dans ngx_lua v0.9.3.

Les tentatives de chargement de fichiers de bytecode Lua 5.1 standard dans des instances ngx_lua liées à LuaJIT 2.0/2.1 (ou vice versa) entraîneront un message d'erreur Nginx tel que celui ci-dessous :

[error] 13909#0: *1 failed to load Lua inlined code: bad byte-code header in /path/to/test_file.luac

Le chargement de fichiers de bytecode via les primitives Lua comme require et dofile devrait toujours fonctionner comme prévu.

Support des variables d'environnement système

Si vous souhaitez accéder à la variable d'environnement système, par exemple, foo, dans Lua via l'API Lua standard os.getenv, vous devez également lister ce nom de variable d'environnement dans votre fichier nginx.conf via la directive env. Par exemple,

 env foo;

Support HTTP 1.0

Le protocole HTTP 1.0 ne prend pas en charge la sortie en morceaux et nécessite un en-tête Content-Length explicite lorsque le corps de la réponse n'est pas vide pour prendre en charge le keep-alive HTTP 1.0. Ainsi, lorsqu'une requête HTTP 1.0 est effectuée et que la directive lua_http10_buffering est activée, ngx_lua mettra en mémoire tampon la sortie des appels ngx.say et ngx.print et retardera également l'envoi des en-têtes de réponse jusqu'à ce que toute la sortie du corps de réponse soit reçue. À ce moment-là, ngx_lua peut calculer la longueur totale du corps et construire un en-tête Content-Length approprié à renvoyer au client HTTP 1.0. Cependant, si l'en-tête de réponse Content-Length est défini dans le code Lua en cours d'exécution, ce tampon sera désactivé même si la directive lua_http10_buffering est activée.

Pour les grandes réponses de sortie en streaming, il est important de désactiver la directive lua_http10_buffering pour minimiser l'utilisation de la mémoire.

Notez que les outils de benchmark HTTP courants tels que ab et http_load émettent des requêtes HTTP 1.0 par défaut. Pour forcer curl à envoyer des requêtes HTTP 1.0, utilisez l'option -0.

Lien statique des modules Lua purs

Avec LuaJIT 2.x, il est possible de lier statiquement le bytecode des modules Lua purs dans l'exécutable Nginx.

Vous pouvez utiliser l'exécutable luajit pour compiler des fichiers de module Lua .lua en fichiers objets .o contenant les données de bytecode exportées, puis lier les fichiers .o directement dans votre construction Nginx.

Voici un exemple trivial pour démontrer cela. Considérons que nous avons le fichier .lua suivant nommé foo.lua :

 -- foo.lua
 local _M = {}

 function _M.go()
     print("Hello from foo")
 end

 return _M

Et ensuite nous compilons ce fichier .lua en fichier foo.o :

 /path/to/luajit/bin/luajit -bg foo.lua foo.o

Ce qui importe ici, c'est le nom du fichier .lua, qui détermine comment vous utilisez ce module plus tard dans le domaine Lua. Le nom de fichier foo.o n'a pas d'importance sauf pour l'extension .o (qui indique à luajit quel format de sortie est utilisé). Si vous souhaitez supprimer les informations de débogage Lua du bytecode résultant, vous pouvez simplement spécifier l'option -b ci-dessus au lieu de -bg.

Ensuite, lors de la construction de Nginx ou d'OpenResty, passez l'option --with-ld-opt="foo.o" au script ./configure :

 ./configure --with-ld-opt="/path/to/foo.o" ...

Enfin, vous pouvez simplement faire ce qui suit dans n'importe quel code Lua exécuté par ngx_lua :

 local foo = require "foo"
 foo.go()

Et ce morceau de code ne dépend plus du fichier externe foo.lua car il a déjà été compilé dans l'exécutable nginx.

Si vous souhaitez utiliser un point dans le nom du module Lua lors de l'appel à require, comme dans

 local foo = require "resty.foo"

vous devez renommer le fichier foo.lua en resty_foo.lua avant de le compiler en fichier .o avec l'utilitaire en ligne de commande luajit.

Il est important d'utiliser exactement la même version de LuaJIT lors de la compilation des fichiers .lua en fichiers .o que lors de la construction de nginx + ngx_lua. Cela est dû au fait que le format de bytecode LuaJIT peut être incompatible entre différentes versions de LuaJIT. Lorsque le format de bytecode est incompatible, vous verrez une erreur d'exécution Lua indiquant que le module Lua n'est pas trouvé.

Lorsque vous avez plusieurs fichiers .lua à compiler et lier, spécifiez simplement leurs fichiers .o en même temps dans la valeur de l'option --with-ld-opt. Par exemple,

 ./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...

Si vous avez trop de fichiers .o, il peut ne pas être faisable de tous les nommer dans une seule commande. Dans ce cas, vous pouvez construire une bibliothèque statique (ou archive) pour vos fichiers .o, comme dans

 ar rcus libmyluafiles.a *.o

ensuite, vous pouvez lier l'archive myluafiles dans son ensemble à votre exécutable nginx :

 ./configure \
     --with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"

/path/to/lib est le chemin du répertoire contenant le fichier libmyluafiles.a. Il convient de noter que l'option de l'éditeur --whole-archive est requise ici car sinon notre archive sera ignorée car aucun symbole de notre archive n'est mentionné dans les parties principales de l'exécutable nginx.

Partage de données au sein d'un travailleur Nginx

Pour partager des données globalement entre toutes les requêtes traitées par le même processus de travail Nginx, encapsulez les données partagées dans un module Lua, utilisez la fonction intégrée Lua require pour importer le module, puis manipulez les données partagées en Lua. Cela fonctionne car les modules Lua requis ne sont chargés qu'une seule fois et toutes les coroutines partageront la même copie du module (à la fois son code et ses données).

Notez que l'utilisation de variables Lua globales est fortement déconseillée, car cela peut entraîner des conditions de concurrence inattendues entre les requêtes concurrentes.

Voici un petit exemple de partage de données au sein d'un travailleur Nginx via un module Lua :

 -- mydata.lua
 local _M = {}

 local data = {
     dog = 3,
     cat = 4,
     pig = 5,
 }

 function _M.get_age(name)
     return data[name]
 end

 return _M

et ensuite y accéder depuis nginx.conf :

 location /lua {
     content_by_lua_block {
         local mydata = require "mydata"
         ngx.say(mydata.get_age("dog"))
     }
 }

Le module mydata dans cet exemple ne sera chargé et exécuté que lors de la première requête vers l'emplacement /lua, et toutes les requêtes suivantes vers le même processus de travail Nginx utiliseront l'instance rechargée du module ainsi que la même copie des données qu'il contient, jusqu'à ce qu'un signal HUP soit envoyé au processus maître Nginx pour forcer un rechargement. Cette technique de partage de données est essentielle pour les applications Lua à haute performance basées sur ce module.

Notez que ce partage de données est à la base par travailleur et non à la base par serveur. C'est-à-dire que lorsqu'il y a plusieurs processus de travail Nginx sous un maître Nginx, le partage de données ne peut pas traverser la frontière des processus entre ces travailleurs.

Il est généralement recommandé de partager des données en lecture seule de cette manière. Vous pouvez également partager des données modifiables entre toutes les requêtes concurrentes de chaque processus de travail Nginx tant qu'il n'y a pas d'opérations I/O non-bloquantes (y compris ngx.sleep) au milieu de vos calculs. Tant que vous ne redonnez pas le contrôle à la boucle d'événements Nginx et au planificateur de threads légers de ngx_lua (même implicitement), il ne peut jamais y avoir de conditions de concurrence entre. Pour cette raison, soyez toujours très prudent lorsque vous souhaitez partager des données modifiables au niveau du travailleur. Des optimisations défectueuses peuvent facilement conduire à des conditions de concurrence difficiles à déboguer sous charge.

Si le partage de données à l'échelle du serveur est requis, utilisez une ou plusieurs des approches suivantes :

  1. Utilisez l'API ngx.shared.DICT fournie par ce module.
  2. Utilisez uniquement un seul travailleur Nginx et un seul serveur (ce qui n'est cependant pas recommandé lorsqu'il y a un CPU multicœur ou plusieurs CPU dans une seule machine).
  3. Utilisez des mécanismes de stockage de données tels que memcached, redis, MySQL ou PostgreSQL. Les versions officielles d'OpenResty sont livrées avec un ensemble de modules Nginx complémentaires et de bibliothèques Lua qui fournissent des interfaces avec ces mécanismes de stockage de données.

Problèmes connus

Problèmes de connexion de socket TCP

La méthode tcpsock:connect peut indiquer un succès malgré des échecs de connexion tels que des erreurs Connection Refused.

Cependant, les tentatives ultérieures de manipulation de l'objet cosocket échoueront et renverront le message d'état d'erreur réel généré par l'opération de connexion échouée.

Ce problème est dû à des limitations dans le modèle d'événements Nginx et n'affecte que Mac OS X.

Yielding/Resuming de Coroutine Lua

  • Parce que les fonctions intégrées dofile et require de Lua sont actuellement implémentées comme des fonctions C dans LuaJIT 2.0/2.1, si le fichier Lua chargé par dofile ou require invoque ngx.location.capture*, ngx.exec, ngx.exit, ou d'autres fonctions API nécessitant un yield dans le scope de niveau supérieur du fichier Lua, alors l'erreur Lua "tentative de yield à travers la frontière d'appel C" sera levée. Pour éviter cela, mettez ces appels nécessitant un yield dans vos propres fonctions Lua dans le fichier Lua au lieu du scope de niveau supérieur du fichier.

Portée des variables Lua

Il faut faire attention lors de l'importation de modules, et cette forme doit être utilisée :

 local xxx = require('xxx')

au lieu de l'ancienne forme obsolète :

 require('xxx')

Voici la raison : par conception, l'environnement global a exactement la même durée de vie que le gestionnaire de requêtes Nginx qui lui est associé. Chaque gestionnaire de requêtes a son propre ensemble de variables globales Lua et c'est l'idée de l'isolation des requêtes. Le module Lua est en fait chargé par le premier gestionnaire de requêtes Nginx et est mis en cache par la fonction intégrée require() dans la table package.loaded pour référence ultérieure, et la fonction module() utilisée par certains modules Lua a l'effet secondaire de définir une variable globale à la table du module chargé. Mais cette variable globale sera effacée à la fin du gestionnaire de requêtes, et chaque gestionnaire de requêtes suivant a son propre environnement global (propre). Ainsi, on obtiendra une exception Lua pour l'accès à la valeur nil.

L'utilisation de variables globales Lua est généralement déconseillée dans le contexte de ngx_lua car :

  1. la mauvaise utilisation des variables globales Lua a des effets secondaires néfastes sur les requêtes concurrentes lorsque ces variables devraient plutôt être locales dans leur portée,
  2. les variables globales Lua nécessitent des recherches dans la table globale qui sont coûteuses en termes de calcul, et
  3. certaines références de variables globales Lua peuvent inclure des erreurs de typage qui rendent leur débogage difficile.

Il est donc fortement recommandé de toujours déclarer de telles variables dans une portée locale appropriée.

 -- Évitez
 foo = 123
 -- Recommandé
 local foo = 123

 -- Évitez
 function foo() return 123 end
 -- Recommandé
 local function foo() return 123 end

Pour trouver toutes les instances de variables globales Lua dans votre code Lua, exécutez l'outil lua-releng sur tous les fichiers source .lua :

$ lua-releng
Vérification de l'utilisation des variables globales Lua dans le fichier lib/foo/bar.lua ...
        1       [1489]  SETGLOBAL       7 -1    ; contient
        55      [1506]  GETGLOBAL       7 -3    ; setvar
        3       [1545]  GETGLOBAL       3 -4    ; varexpand

La sortie indique que la ligne 1489 du fichier lib/foo/bar.lua écrit dans une variable globale nommée contains, la ligne 1506 lit à partir de la variable globale setvar, et la ligne 1545 lit la variable globale varexpand.

Cet outil garantira que les variables locales dans les fonctions de module Lua sont toutes déclarées avec le mot-clé local, sinon une exception d'exécution sera levée. Cela empêche des conditions de concurrence indésirables lors de l'accès à de telles variables. Voir Partage de données au sein d'un travailleur Nginx pour les raisons derrière cela.

Emplacements configurés par des directives de sous-requête d'autres modules

Les directives ngx.location.capture et ngx.location.capture_multi ne peuvent pas capturer des emplacements qui incluent les directives add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest, ou echo_subrequest_async.

 location /foo {
     content_by_lua_block {
         res = ngx.location.capture("/bar")
     }
 }
 location /bar {
     echo_location /blah;
 }
 location /blah {
     echo "Succès !";
 }
 $ curl -i http://example.com/foo

ne fonctionnera pas comme prévu.

Cosockets non disponibles partout

En raison de limitations internes dans le noyau Nginx, l'API cosocket est désactivée dans les contextes suivants : set_by_lua*, log_by_lua*, header_filter_by_lua*, et body_filter_by_lua.

Les cosockets sont actuellement également désactivés dans les contextes des directives init_by_lua* et init_worker_by_lua* mais nous pourrions ajouter le support pour ces contextes à l'avenir car il n'y a pas de limitation dans le noyau Nginx (ou la limitation pourrait être contournée).

Cependant, il existe un contournement lorsque le contexte d'origine n'a pas besoin d'attendre les résultats du cosocket. C'est-à-dire, créer un minuteur sans délai via l'API ngx.timer.at et effectuer les résultats du cosocket dans le gestionnaire de minuteur, qui s'exécute de manière asynchrone par rapport au contexte d'origine créant le minuteur.

Séquences d'échappement spéciales

REMARQUE Suite à la version v0.9.17, ce piège peut être évité en utilisant les directives de configuration *_by_lua_block {}.

Les séquences PCRE telles que \d, \s, ou \w, nécessitent une attention particulière car dans les littéraux de chaîne, le caractère de barre oblique inversée, \, est supprimé à la fois par le parseur de langage Lua et par le parseur de fichier de configuration Nginx avant traitement s'il n'est pas dans une directive *_by_lua_block {}. Ainsi, le code suivant ne fonctionnera pas comme prévu :

 # nginx.conf
 ? location /test {
 ?     content_by_lua '
 ?         local regex = "\d+"  -- CECI EST FAUX EN DEHORS D'UNE DIRECTIVE *_by_lua_block
 ?         local m = ngx.re.match("hello, 1234", regex)
 ?         if m then ngx.say(m[0]) else ngx.say("non apparié !") end
 ?     ';
 ? }
 # évalue à "non apparié !"

Pour éviter cela, double échappez la barre oblique inversée :

 # nginx.conf
 location /test {
     content_by_lua '
         local regex = "\\\\d+"
         local m = ngx.re.match("hello, 1234", regex)
         if m then ngx.say(m[0]) else ngx.say("non apparié !") end
     ';
 }
 # évalue à "1234"

Ici, \\\\d+ est réduit à \\d+ par le parseur de fichier de configuration Nginx et cela est encore réduit à \d+ par le parseur de langage Lua avant l'exécution.

Alternativement, le motif regex peut être présenté sous forme de littéral de chaîne Lua entre crochets longs, [[...]], auquel cas les barres obliques inversées doivent uniquement être échappées une fois pour le parseur de fichier de configuration Nginx.

 # nginx.conf
 location /test {
     content_by_lua '
         local regex = [[\\d+]]
         local m = ngx.re.match("hello, 1234", regex)
         if m then ngx.say(m[0]) else ngx.say("non apparié !") end
     ';
 }
 # évalue à "1234"

Ici, [[\\d+]] est réduit à [[\d+]] par le parseur de fichier de configuration Nginx et cela est traité correctement.

Notez qu'une forme plus longue de crochets longs, [=[...]=], peut être nécessaire si le motif regex contient des séquences [...]. La forme [=[...]=] peut être utilisée comme forme par défaut si désiré.

 # nginx.conf
 location /test {
     content_by_lua '
         local regex = [=[[0-9]+]=]
         local m = ngx.re.match("hello, 1234", regex)
         if m then ngx.say(m[0]) else ngx.say("non apparié !") end
     ';
 }
 # évalue à "1234"

Une approche alternative pour échapper les séquences PCRE consiste à s'assurer que le code Lua est placé dans des fichiers de script externes et exécuté à l'aide des différentes directives *_by_lua_file. Avec cette approche, les barres obliques inversées ne sont supprimées que par le parseur de langage Lua et doivent donc être échappées une fois chacune.

 -- test.lua
 local regex = "\\d+"
 local m = ngx.re.match("hello, 1234", regex)
 if m then ngx.say(m[0]) else ngx.say("non apparié !") end
 -- évalue à "1234"

Dans les fichiers de script externes, les séquences PCRE présentées sous forme de littéraux de chaîne Lua longs ne nécessitent pas de modification.

 -- test.lua
 local regex = [[\d+]]
 local m = ngx.re.match("hello, 1234", regex)
 if m then ngx.say(m[0]) else ngx.say("non apparié !") end
 -- évalue à "1234"

Comme mentionné précédemment, les séquences PCRE présentées dans les directives *_by_lua_block {} (disponibles après la version v0.9.17) ne nécessitent pas de modification.

 # nginx.conf
 location /test {
     content_by_lua_block {
         local regex = [[\d+]]
         local m = ngx.re.match("hello, 1234", regex)
         if m then ngx.say(m[0]) else ngx.say("non apparié !") end
     }
 }
 # évalue à "1234"

REMARQUE Il est recommandé d'utiliser by_lua_file lorsque le code Lua est très long.

Mélange avec SSI non pris en charge

Le mélange de SSI avec ngx_lua dans la même requête Nginx n'est pas du tout pris en charge. Utilisez simplement ngx_lua exclusivement. Tout ce que vous pouvez faire avec SSI peut être fait avec ngx_lua de toute façon et cela peut être plus efficace en utilisant ngx_lua.

Mode SPDY non entièrement pris en charge

Certaines API Lua fournies par ngx_lua ne fonctionnent pas encore en mode SPDY de Nginx : ngx.location.capture, ngx.location.capture_multi, et ngx.req.socket.

Données manquantes sur les requêtes court-circuitées

Nginx peut terminer une requête tôt avec (au moins) :

  • 400 (Mauvaise requête)
  • 405 (Non autorisé)
  • 408 (Délai d'attente de la requête)
  • 413 (Entité de requête trop grande)
  • 414 (URI de requête trop grande)
  • 494 (En-têtes de requête trop grands)
  • 499 (Client a fermé la requête)
  • 500 (Erreur interne du serveur)
  • 501 (Non implémenté)

Cela signifie que les phases qui s'exécutent normalement sont sautées, telles que la phase de réécriture ou d'accès. Cela signifie également que les phases ultérieures qui s'exécutent quoi qu'il arrive, par exemple log_by_lua, n'auront pas accès aux informations qui sont normalement définies dans ces phases.

Changements

Les changements apportés à chaque version de ce module sont listés dans les journaux de modifications du bundle OpenResty :

https://openresty.org/#Changes

Test Suite

Les dépendances suivantes sont requises pour exécuter la suite de tests :

L'ordre dans lequel ces modules sont ajoutés lors de la configuration est important car la position de tout module de filtre dans la chaîne de filtrage détermine la sortie finale, par exemple. L'ordre d'ajout correct est montré ci-dessus.

  • Bibliothèques Lua tierces :

  • Applications :

    • mysql : créer la base de données 'ngx_test', accorder tous les privilèges à l'utilisateur 'ngx_test', le mot de passe est 'ngx_test'
    • memcached : écoute sur le port par défaut, 11211.
    • redis : écoute sur le port par défaut, 6379.

Voir aussi le script de construction pour développeurs pour plus de détails sur la configuration de l'environnement de test.

Pour exécuter l'ensemble de la suite de tests en mode de test par défaut :

cd /path/to/lua-nginx-module
export PATH=/path/to/your/nginx/sbin:$PATH
prove -I/path/to/test-nginx/lib -r t

Pour exécuter des fichiers de test spécifiques :

cd /path/to/lua-nginx-module
export PATH=/path/to/your/nginx/sbin:$PATH
prove -I/path/to/test-nginx/lib t/002-content.t t/003-errors.t

Pour exécuter un bloc de test spécifique dans un fichier de test particulier, ajoutez la ligne --- ONLY au bloc de test que vous souhaitez exécuter, puis utilisez l'utilitaire prove pour exécuter ce fichier .t.

Il existe également divers modes de test basés sur mockeagain, valgrind, etc. Référez-vous à la documentation Test::Nginx pour plus de détails sur divers modes de test avancés. Voir également les rapports de test pour le cluster de test Nginx fonctionnant sur Amazon EC2 : https://qa.openresty.org.

Voir aussi

Articles de blog :

Autres modules et bibliothèques connexes :

Directives

Les blocs de construction de base du scripting Nginx avec Lua sont les directives. Les directives sont utilisées pour spécifier quand le code Lua utilisateur est exécuté et comment le résultat sera utilisé. Ci-dessous se trouve un diagramme montrant l'ordre dans lequel les directives sont exécutées.

Directives des modules Lua Nginx

lua_load_resty_core

syntax: lua_load_resty_core on|off

default: lua_load_resty_core on

context: http

Cette directive est obsolète depuis la version v0.10.16 de ce module. Le module resty.core de lua-resty-core est maintenant chargé de manière obligatoire lors de l'initialisation de la VM Lua. Spécifier cette directive n'aura aucun effet.

Cette directive a été introduite pour la première fois dans la version v0.10.15 et était utilisée pour charger optionnellement le module resty.core.

Retour au sommaire

lua_capture_error_log

syntax: lua_capture_error_log size

default: aucun

context: http

Active un tampon de la taille spécifiée pour capturer toutes les données de message de journal d'erreur Nginx (pas seulement celles produites par ce module ou le sous-système http de Nginx, mais tout) sans toucher aux fichiers ou aux disques.

Vous pouvez utiliser des unités comme k et m dans la valeur size, comme dans

 lua_capture_error_log 100k;

En règle générale, un tampon de 4 Ko peut généralement contenir environ 20 messages de journal d'erreur typiques. Donc, faites le calcul !

Ce tampon ne grandit jamais. S'il est plein, de nouveaux messages de journal d'erreur remplaceront les plus anciens dans le tampon.

La taille du tampon doit être supérieure à la longueur maximale d'un message de journal d'erreur unique (qui est de 4 Ko dans OpenResty et de 2 Ko dans NGINX standard).

Vous pouvez lire les messages dans le tampon sur le terrain Lua via la fonction get_logs() du module ngx.errlog de la bibliothèque lua-resty-core. Cette fonction API Lua renverra les messages de journal d'erreur capturés et supprimera également ceux déjà lus du tampon de capture global, libérant de la place pour de nouvelles données de journal d'erreur. Pour cette raison, l'utilisateur ne doit pas configurer ce tampon pour qu'il soit trop grand si l'utilisateur lit les données de journal d'erreur mises en mémoire tampon assez rapidement.

Notez que le niveau de journal spécifié dans la directive standard error_log a un effet sur cette installation de capture. Il ne capture que les messages de journal d'un niveau pas inférieur au niveau de journal spécifié dans la directive error_log. L'utilisateur peut toujours choisir de définir un niveau de filtrage de journal encore plus élevé à la volée via la fonction API Lua errlog.set_filter_level. Ainsi, c'est plus flexible que la directive statique error_log.

Il convient de noter qu'il n'existe aucun moyen de capturer les journaux de débogage sans construire OpenResty ou Nginx avec l'option ./configure --with-debug. Et activer les journaux de débogage est fortement déconseillé dans les versions de production en raison de la surcharge élevée.

Cette directive a été introduite pour la première fois dans la version v0.10.9.

Retour au sommaire

lua_use_default_type

syntax: lua_use_default_type on | off

default: lua_use_default_type on

context: http, server, location, location if

Spécifie s'il faut utiliser le type MIME spécifié par la directive default_type pour la valeur par défaut de l'en-tête de réponse Content-Type. Désactivez cette directive si un en-tête de réponse Content-Type par défaut pour les gestionnaires de requêtes Lua n'est pas souhaité.

Cette directive est activée par défaut.

Cette directive a été introduite pour la première fois dans la version v0.9.1.

Retour au sommaire

lua_malloc_trim

syntax: lua_malloc_trim <request-count>

default: lua_malloc_trim 1000

context: http

Demande à la bibliothèque d'exécution libc sous-jacente de libérer sa mémoire libre mise en cache vers le système d'exploitation tous les N requêtes traitées par le noyau Nginx. Par défaut, N est 1000. Vous pouvez configurer le nombre de requêtes en utilisant vos propres chiffres. Des nombres plus petits signifient des libérations plus fréquentes, ce qui peut entraîner une consommation de temps CPU plus élevée et une empreinte mémoire plus petite, tandis que des nombres plus élevés entraînent généralement moins de surcharge CPU et une empreinte mémoire relativement plus grande. Ajustez le nombre pour vos propres cas d'utilisation.

Configurer l'argument à 0 désactive essentiellement complètement le nettoyage périodique de la mémoire.

 lua_malloc_trim 0;  # désactiver complètement le nettoyage

L'implémentation actuelle utilise un gestionnaire de phase de journal Nginx pour effectuer le comptage des requêtes. Ainsi, l'apparition des directives log_subrequest on dans nginx.conf peut accélérer le comptage lorsque des sous-requêtes sont impliquées. Par défaut, seuls les "requêtes principales" sont comptées.

Notez que cette directive n'affecte pas la mémoire allouée par l'allocateur propre de LuaJIT basé sur l'appel système mmap.

Cette directive a été introduite pour la première fois dans la version v0.10.7.

Retour au sommaire

lua_code_cache

syntax: lua_code_cache on | off

default: lua_code_cache on

context: http, server, location, location if

Active ou désactive le cache de code Lua pour le code Lua dans les directives *_by_lua_file (comme set_by_lua_file et content_by_lua_file) et les modules Lua.

Lorsqu'il est désactivé, chaque requête servie par ngx_lua s'exécutera dans une instance de VM Lua distincte, à partir de la version 0.9.3. Ainsi, les fichiers Lua référencés dans set_by_lua_file, content_by_lua_file, access_by_lua_file, etc. ne seront pas mis en cache et tous les modules Lua utilisés seront chargés à partir de zéro. Avec cela en place, les développeurs peuvent adopter une approche d'édition et de rafraîchissement.

Veuillez noter cependant que le code Lua écrit en ligne dans nginx.conf tel que ceux spécifiés par set_by_lua, content_by_lua, access_by_lua, et rewrite_by_lua ne sera pas mis à jour lorsque vous éditez le code Lua en ligne dans votre fichier nginx.conf car seul le parseur de fichier de configuration Nginx peut correctement analyser le fichier nginx.conf et le seul moyen est de recharger le fichier de configuration en envoyant un signal HUP ou simplement en redémarrant Nginx.

Même lorsque le cache de code est activé, les fichiers Lua qui sont chargés par dofile ou loadfile dans *_by_lua_file ne peuvent pas être mis en cache (à moins que vous ne mettez vous-même en cache les résultats). En général, vous pouvez soit utiliser les directives init_by_lua ou init_by_lua_file pour charger tous ces fichiers ou simplement faire de ces fichiers Lua de véritables modules Lua et les charger via require.

Le module ngx_lua ne prend pas en charge le mode stat disponible avec le module Apache mod_lua (pour l'instant).

Désactiver le cache de code Lua est fortement déconseillé pour une utilisation en production et ne doit être utilisé que pendant le développement car cela a un impact négatif significatif sur les performances globales. Par exemple, les performances d'un exemple Lua "hello world" peuvent chuter d'un ordre de grandeur après la désactivation du cache de code Lua.

Retour au sommaire

lua_thread_cache_max_entries

syntax: lua_thread_cache_max_entries <num>

default: lua_thread_cache_max_entries 1024

context: http

Spécifie le nombre maximal d'entrées autorisées dans le cache d'objets de thread Lua au niveau du processus de travail.

Ce cache recycle les objets GC de thread Lua parmi tous nos "threads légers".

Une valeur zéro de <num> désactive le cache.

Notez que cette fonctionnalité nécessite LuaJIT d'OpenResty avec la nouvelle API C lua_resetthread.

Cette fonctionnalité a été introduite pour la première fois dans la version v0.10.9.

Retour au sommaire

lua_regex_cache_max_entries

syntax: lua_regex_cache_max_entries <num>

default: lua_regex_cache_max_entries 1024

context: http

Spécifie le nombre maximal d'entrées autorisées dans le cache regex compilé au niveau du processus de travail.

Les expressions régulières utilisées dans ngx.re.match, ngx.re.gmatch, ngx.re.sub, et ngx.re.gsub seront mises en cache dans ce cache si l'option regex o (c'est-à-dire, le drapeau compile-once) est spécifiée.

Le nombre d'entrées autorisées par défaut est 1024 et lorsque cette limite est atteinte, de nouvelles expressions régulières ne seront pas mises en cache (comme si l'option o n'était pas spécifiée) et il y aura un avertissement dans le fichier error.log :

2011/08/27 23:18:26 [warn] 31997#0: *1 lua exceeding regex cache max entries (1024), ...

Si vous utilisez l'implémentation ngx.re.* de lua-resty-core en chargeant le module resty.core.regex (ou juste le module resty.core), alors un cache LRU est utilisé pour le cache regex utilisé ici.

N'activez pas l'option o pour les expressions régulières (et/ou les arguments de chaîne replace pour ngx.re.sub et ngx.re.gsub) qui sont générées à la volée et donnent lieu à des variations infinies pour éviter d'atteindre la limite spécifiée.

Retour au sommaire

lua_regex_match_limit

syntax: lua_regex_match_limit <num>

default: lua_regex_match_limit 0

context: http

Spécifie la "limite de correspondance" utilisée par la bibliothèque PCRE lors de l'exécution de l'API ngx.re. Pour citer la page de manuel PCRE, "la limite ... a pour effet de limiter la quantité de retour en arrière qui peut avoir lieu."

Lorsque la limite est atteinte, la chaîne d'erreur "pcre_exec() a échoué : -8" sera renvoyée par les fonctions de l'API ngx.re.

Lorsque la limite est définie à 0, la "limite de correspondance" par défaut lors de la compilation de la bibliothèque PCRE est utilisée. Et c'est la valeur par défaut de cette directive.

Cette directive a été introduite pour la première fois dans la version v0.8.5.

Retour au sommaire

lua_package_path

syntax: lua_package_path <lua-style-path-str>

default: Le contenu de la variable d'environnement LUA_PATH ou les valeurs par défaut compilées de Lua.

context: http

Définit le chemin de recherche des modules Lua utilisés par les scripts spécifiés par set_by_lua, content_by_lua et d'autres. La chaîne de chemin est au format de chemin standard de Lua, et ;; peut être utilisé pour représenter les chemins de recherche d'origine.

À partir de la version v0.5.0rc29, la notation spéciale $prefix ou ${prefix} peut être utilisée dans la chaîne de chemin de recherche pour indiquer le chemin du préfixe du serveur généralement déterminé par l'option de ligne de commande -p PATH lors du démarrage du serveur Nginx.

Retour au sommaire

lua_package_cpath

syntax: lua_package_cpath <lua-style-cpath-str>

default: Le contenu de la variable d'environnement LUA_CPATH ou les valeurs par défaut compilées de Lua.

context: http

Définit le chemin de recherche des modules C Lua utilisés par les scripts spécifiés par set_by_lua, content_by_lua et d'autres. La chaîne cpath est au format standard de cpath Lua, et ;; peut être utilisé pour représenter le cpath d'origine.

À partir de la version v0.5.0rc29, la notation spéciale $prefix ou ${prefix} peut être utilisée dans la chaîne de chemin de recherche pour indiquer le chemin du préfixe du serveur généralement déterminé par l'option de ligne de commande -p PATH lors du démarrage du serveur Nginx.

Retour au sommaire

init_by_lua

syntax: init_by_lua <lua-script-str>

context: http

phase: loading-config

REMARQUE L'utilisation de cette directive est déconseillée après la version v0.9.17. Utilisez la directive init_by_lua_block à la place.

Similaire à la directive init_by_lua_block, mais accepte le code source Lua directement dans une chaîne littérale Nginx (ce qui nécessite un échappement de caractères spécial).

Par exemple,

 init_by_lua '
     print("Je n'ai pas besoin d'échappement supplémentaire ici, par exemple : \r\nblah")
 '

Cette directive a été introduite pour la première fois dans la version v0.5.5.

Retour au sommaire

init_by_lua_block

syntax: init_by_lua_block { lua-script }

context: http

phase: loading-config

Lorsque Nginx reçoit le signal HUP et commence à recharger le fichier de configuration, la VM Lua sera également recréée et init_by_lua_block s'exécutera à nouveau sur la nouvelle VM Lua. Dans le cas où la directive lua_code_cache est désactivée (activée par défaut), le gestionnaire init_by_lua_block s'exécutera à chaque requête car dans ce mode spécial, une VM Lua autonome est toujours créée pour chaque requête.

En général, vous pouvez précharger des modules Lua au démarrage du serveur grâce à ce crochet et tirer parti de l'optimisation de copie à l'écriture (COW) des systèmes d'exploitation modernes. Voici un exemple pour précharger des modules Lua :

 # cela s'exécute avant de forker les processus de travail nginx :
 init_by_lua_block { require "cjson" }

 server {
     location = /api {
         content_by_lua_block {
             -- le require() suivant ne fera que retourner
             -- le module déjà chargé de package.loaded :
             ngx.say(require "cjson".encode{dog = 5, cat = 6})
         }
     }
 }

Vous pouvez également initialiser le stockage shm lua_shared_dict à cette phase. Voici un exemple pour cela :

 lua_shared_dict dogs 1m;

 init_by_lua_block {
     local dogs = ngx.shared.dogs
     dogs:set("Tom", 56)
 }

 server {
     location = /api {
         content_by_lua_block {
             local dogs = ngx.shared.dogs
             ngx.say(dogs:get("Tom"))
         }
     }
 }

Mais notez que, le stockage shm de lua_shared_dict ne sera pas effacé lors d'un rechargement de configuration (via le signal HUP, par exemple). Donc, si vous ne souhaitez pas réinitialiser le stockage shm dans votre code init_by_lua_block dans ce cas, vous devez simplement définir un indicateur personnalisé dans le stockage shm et toujours vérifier l'indicateur dans votre code init_by_lua_block.

Parce que le code Lua dans ce contexte s'exécute avant que Nginx fork ses processus de travail (le cas échéant), les données ou le code chargés ici bénéficieront de la fonctionnalité Copy-on-write (COW) fournie par de nombreux systèmes d'exploitation parmi tous les processus de travail, économisant ainsi beaucoup de mémoire.

Ne pas initialiser vos propres variables globales Lua dans ce contexte car