# *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.
-
Vidéo YouTube "Hello World HTTP Example with OpenResty/Lua"
-
Vidéo YouTube "Write Your Own Lua Modules in OpenResty/Nginx Applications"
-
Vidéo YouTube "OpenResty's resty Command-Line Utility Demo"
-
Vidéo YouTube "Measure Execution Time of Lua Code Correctly in OpenResty"
-
Vidéo YouTube "Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup"
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 :
- lua-resty-memcached
- lua-resty-mysql
- lua-resty-redis
- lua-resty-dns
- lua-resty-upload
- lua-resty-websocket
- lua-resty-lock
- lua-resty-logger-socket
- lua-resty-lrucache
- lua-resty-string
- ngx_memc
- ngx_postgres
- ngx_redis2
- ngx_redis
- ngx_proxy
- ngx_fastcgi
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"
À 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"
où /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 :
- Utilisez l'API ngx.shared.DICT fournie par ce module.
- 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).
- Utilisez des mécanismes de stockage de données tels que
memcached,redis,MySQLouPostgreSQL. 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
dofileetrequirede Lua sont actuellement implémentées comme des fonctions C dans LuaJIT 2.0/2.1, si le fichier Lua chargé pardofileourequireinvoque 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 :
- 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,
- les variables globales Lua nécessitent des recherches dans la table globale qui sont coûteuses en termes de calcul, et
- 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 :
-
Version Nginx >= 1.4.2
-
Modules Perl :
- Test::Nginx : https://github.com/openresty/test-nginx
-
Modules Nginx :
- ngx_devel_kit
- ngx_set_misc
- ngx_auth_request (ceci n'est pas nécessaire si vous utilisez Nginx 1.5.4+).
- ngx_echo
- ngx_memc
- ngx_srcache
- ngx_lua (c'est-à-dire, ce module)
- ngx_lua_upstream
- ngx_headers_more
- ngx_drizzle
- ngx_rds_json
- ngx_coolkit
- ngx_redis2
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 :
- Introduction aux graphiques de flamme CPU Lua-Land
- Comment OpenResty et Nginx allouent et gèrent la mémoire
- Comment OpenResty et les zones de mémoire partagée de Nginx consomment de la RAM
- Fragmentation de la mémoire dans OpenResty et les zones de mémoire partagée de Nginx
Autres modules et bibliothèques connexes :
- ngx_stream_lua_module pour un port officiel de ce module pour le sous-système "stream" de Nginx (effectuant des communications TCP en aval génériques).
- lua-resty-memcached bibliothèque basée sur le cosocket ngx_lua.
- lua-resty-redis bibliothèque basée sur le cosocket ngx_lua.
- lua-resty-mysql bibliothèque basée sur le cosocket ngx_lua.
- lua-resty-upload bibliothèque basée sur le cosocket ngx_lua.
- lua-resty-dns bibliothèque basée sur le cosocket ngx_lua.
- lua-resty-websocket bibliothèque pour le serveur et le client WebSocket, basée sur le cosocket ngx_lua.
- lua-resty-string bibliothèque basée sur LuaJIT FFI.
- lua-resty-lock bibliothèque pour une API de verrou simple non-bloquante.
- lua-resty-cookie bibliothèque pour la manipulation des cookies HTTP.
- Acheminer les requêtes vers différentes requêtes MySQL en fonction des arguments URI
- Routage dynamique basé sur Redis et Lua
- Utiliser LuaRocks avec ngx_lua
- Introduction à ngx_lua
- ngx_devel_kit
- echo-nginx-module
- drizzle-nginx-module
- postgres-nginx-module
- memc-nginx-module
- Le bundle OpenResty
- Nginx Systemtap Toolkit
Directives
- lua_load_resty_core
- lua_capture_error_log
- lua_use_default_type
- lua_malloc_trim
- lua_code_cache
- lua_thread_cache_max_entries
- lua_regex_cache_max_entries
- lua_regex_match_limit
- lua_package_path
- lua_package_cpath
- init_by_lua
- init_by_lua_block
- init_by_lua_file
- init_worker_by_lua
- init_worker_by_lua_block
- init_worker_by_lua_file
- exit_worker_by_lua_block
- exit_worker_by_lua_file
- set_by_lua
- set_by_lua_block
- set_by_lua_file
- precontent_by_lua_block
- precontent_by_lua_file
- content_by_lua
- content_by_lua_block
- content_by_lua_file
- server_rewrite_by_lua_block
- server_rewrite_by_lua_file
- rewrite_by_lua
- rewrite_by_lua_block
- rewrite_by_lua_file
- access_by_lua
- access_by_lua_block
- access_by_lua_file
- header_filter_by_lua
- header_filter_by_lua_block
- header_filter_by_lua_file
- body_filter_by_lua
- body_filter_by_lua_block
- body_filter_by_lua_file
- log_by_lua
- log_by_lua_block
- log_by_lua_file
- balancer_by_lua_block
- balancer_by_lua_file
- balancer_keepalive
- lua_need_request_body
- ssl_client_hello_by_lua_block
- ssl_client_hello_by_lua_file
- ssl_certificate_by_lua_block
- ssl_certificate_by_lua_file
- ssl_session_fetch_by_lua_block
- ssl_session_fetch_by_lua_file
- ssl_session_store_by_lua_block
- ssl_session_store_by_lua_file
- proxy_ssl_certificate_by_lua_block
- proxy_ssl_certificate_by_lua_file
- proxy_ssl_verify_by_lua_block
- proxy_ssl_verify_by_lua_file
- lua_shared_dict
- lua_socket_connect_timeout
- lua_socket_send_timeout
- lua_socket_send_lowat
- lua_socket_read_timeout
- lua_socket_buffer_size
- lua_socket_pool_size
- lua_socket_keepalive_timeout
- lua_socket_log_errors
- lua_ssl_ciphers
- lua_ssl_crl
- lua_ssl_protocols
- lua_ssl_certificate
- lua_ssl_certificate_key
- lua_ssl_trusted_certificate
- lua_ssl_verify_depth
- lua_ssl_key_log
- lua_ssl_conf_command
- lua_upstream_skip_openssl_default_verify
- lua_http10_buffering
- rewrite_by_lua_no_postpone
- access_by_lua_no_postpone
- precontent_by_lua_no_postpone
- lua_transform_underscores_in_response_headers
- lua_check_client_abort
- lua_max_pending_timers
- lua_max_running_timers
- lua_sa_restart
- lua_worker_thread_vm_pool_size
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.

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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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




