Saltar a contenido

lua: Soporte de scripting Lua para NGINX

Instalación

Puedes instalar este módulo en cualquier distribución basada en RHEL, incluyendo, pero no limitado a:

  • RedHat Enterprise Linux 7, 8, 9 y 10
  • CentOS 7, 8, 9
  • AlmaLinux 8, 9
  • Rocky Linux 8, 9
  • Amazon Linux 2 y Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install nginx-module-lua
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

Habilita el módulo agregando lo siguiente en la parte superior de /etc/nginx/nginx.conf:

load_module modules/ngx_http_lua_module.so;

Este documento describe nginx-module-lua v0.10.31 lanzado el 29 de mayo de 2026.


Te invitamos a suscribirte a nuestro canal oficial de YouTube, OpenResty.

Sinopsis

 # establecer rutas de búsqueda para bibliotecas externas de Lua puras (';;' es la ruta predeterminada):

 # establecer rutas de búsqueda para bibliotecas externas de Lua escritas en C (también se puede usar ';;'):

 server {
     location /lua_content {
         # tipo MIME determinado por default_type:
         default_type 'text/plain';

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

     location /nginx_var {
         # tipo MIME determinado por default_type:
         default_type 'text/plain';

         # intentar acceder a /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()  -- leer explícitamente el cuerpo de la solicitud
             local data = ngx.req.get_body_data()
             if data then
                 ngx.say("body data:")
                 ngx.print(data)
                 return
             end

             -- el cuerpo puede ser almacenado en un archivo temporal:
             local file = ngx.req.get_body_file()
             if file then
                 ngx.say("body is in file ", file)
             else
                 ngx.say("no body found")
             end
         }
     }

     # I/O no bloqueante transparente en Lua a través de subsolicitudes
     # (bueno, una mejor manera es usar cosockets)
     location = /lua {
         # tipo MIME determinado por default_type:
         default_type 'text/plain';

         content_by_lua_block {
             local res = ngx.location.capture("/some_other_location")
             if res then
                 ngx.say("status: ", res.status)
                 ngx.say("body:")
                 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;
     }

     # usar la variable de nginx en la ruta del código
     # PRECAUCIÓN: los contenidos en la variable de nginx deben ser filtrados cuidadosamente,
     # de lo contrario habrá un gran riesgo de seguridad.
     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 {
            -- verificar si la dirección IP del cliente está en nuestra lista negra
            if ngx.var.remote_addr == "132.5.72.3" then
                ngx.exit(ngx.HTTP_FORBIDDEN)
            end

            -- verificar si la URI contiene palabras inapropiadas
            if ngx.var.uri and
                   string.match(ngx.var.request_body, "evil")
            then
                return ngx.redirect("/terms_of_use.html")
            end

            -- pruebas pasadas
        }

        # configuraciones proxy_pass/fastcgi_pass/etc.
     }
 }

Descripción

Este módulo incrusta LuaJIT 2.0/2.1 en Nginx. Es un componente central de OpenResty. Si estás usando este módulo, entonces esencialmente estás usando OpenResty.

Desde la versión v0.10.16 de este módulo, el intérprete estándar de Lua (también conocido como "PUC-Rio Lua") ya no es compatible. Este documento usa de manera intercambiable los términos "Lua" y "LuaJIT" para referirse al intérprete LuaJIT.

Al aprovechar las subsolicitudes de Nginx, este módulo permite la integración de los poderosos hilos de Lua (conocidos como "corutinas" de Lua) en el modelo de eventos de Nginx.

A diferencia de mod_lua de Apache y mod_magnet de Lighttpd, el código Lua ejecutado utilizando este módulo puede ser 100% no bloqueante en el tráfico de red siempre que se use la API de Nginx para Lua proporcionada por este módulo para manejar solicitudes a servicios upstream como MySQL, PostgreSQL, Memcached, Redis o servicios web HTTP upstream.

Al menos las siguientes bibliotecas de Lua y módulos de Nginx pueden ser utilizados con este módulo:

Casi cualquier módulo de Nginx puede ser utilizado con este módulo ngx_lua mediante ngx.location.capture o ngx.location.capture_multi, pero se recomienda usar esas bibliotecas lua-resty-* en lugar de crear subsolicitudes para acceder a los módulos upstream de Nginx porque las primeras son generalmente mucho más flexibles y eficientes en memoria.

El intérprete de Lua (también conocido como "Estado de Lua" o "instancia de VM de LuaJIT") se comparte entre todas las solicitudes en un único proceso de trabajo de Nginx para minimizar el uso de memoria. Los contextos de solicitud se segregan utilizando corutinas ligeras de Lua.

Los módulos de Lua cargados persisten en el nivel del proceso de trabajo de Nginx, resultando en una pequeña huella de memoria en Lua incluso bajo cargas pesadas.

Este módulo está integrado en el subsistema "http" de Nginx, por lo que solo puede comunicarse utilizando protocolos de comunicación descendente en la familia HTTP (HTTP 0.9/1.0/1.1/2.0, WebSockets, etc...). Si deseas realizar comunicaciones TCP genéricas con los clientes descendentes, entonces deberías usar el módulo ngx_stream_lua en su lugar, que ofrece una API de Lua compatible.

Usos Típicos

Solo por nombrar algunos:

  • Combinar y procesar salidas de varios upstream de Nginx (proxy, drizzle, postgres, redis, memcached, etc.) en Lua,
  • realizar controles de acceso y seguridad arbitrariamente complejos en Lua antes de que las solicitudes lleguen a los backends upstream,
  • manipular encabezados de respuesta de manera arbitraria (por Lua),
  • obtener información de backend de almacenamiento externo (como redis, memcached, mysql, postgresql) y usar esa información para elegir qué backend upstream acceder sobre la marcha,
  • codificar aplicaciones web arbitrariamente complejas en un manejador de contenido utilizando acceso sincrónico pero aún no bloqueante a los backends de bases de datos y otros almacenamiento,
  • realizar un despacho de URL muy complejo en Lua en la fase de reescritura,
  • usar Lua para implementar mecanismos de caché avanzados para las subsolicitudes de Nginx y ubicaciones arbitrarias.

Las posibilidades son ilimitadas ya que el módulo permite reunir varios elementos dentro de Nginx así como exponer el poder del lenguaje Lua al usuario. El módulo proporciona la flexibilidad total de scripting mientras ofrece niveles de rendimiento comparables con programas nativos en C tanto en términos de tiempo de CPU como de huella de memoria gracias a LuaJIT 2.x.

Otras implementaciones de lenguajes de scripting típicamente luchan por igualar este nivel de rendimiento.

Compatibilidad con Nginx

La última versión de este módulo es compatible con las siguientes versiones de Nginx:

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

Los núcleos de Nginx anteriores a la versión 1.6.0 (exclusivo) no son compatibles.

Repositorio de Código

El repositorio de código de este proyecto está alojado en GitHub en openresty/lua-nginx-module.

Soporte de bytecode de LuaJIT

Mira el video de YouTube "Measure Execution Time of Lua Code Correctly in OpenResty"

Precompile Lua Modules into LuaJIT Bytecode to Speedup OpenResty Startup

A partir de la versión v0.5.0rc32, todas las directivas de configuración *_by_lua_file (como content_by_lua_file) admiten la carga de archivos de bytecode en bruto de LuaJIT 2.0/2.1 directamente:

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

La opción -bg se puede usar para incluir información de depuración en el archivo de bytecode de LuaJIT:

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

Consulta la documentación oficial de LuaJIT sobre la opción -b para más detalles:

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

Ten en cuenta que los archivos de bytecode generados por LuaJIT 2.1 no son compatibles con LuaJIT 2.0, y viceversa. El soporte para bytecode de LuaJIT 2.1 se agregó por primera vez en ngx_lua v0.9.3.

Los intentos de cargar archivos de bytecode estándar de Lua 5.1 en instancias de ngx_lua vinculadas a LuaJIT 2.0/2.1 (o viceversa) resultarán en un mensaje de error de Nginx como el siguiente:

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

Cargar archivos de bytecode a través de las primitivas de Lua como require y dofile debería funcionar siempre como se espera.

Soporte de Variables de Entorno del Sistema

Si deseas acceder a la variable de entorno del sistema, digamos, foo, en Lua a través de la API estándar de Lua os.getenv, entonces también deberías listar este nombre de variable de entorno en tu archivo nginx.conf a través de la directiva env. Por ejemplo,

 env foo;

Soporte para HTTP 1.0

El protocolo HTTP 1.0 no admite salida en fragmentos y requiere un encabezado Content-Length explícito cuando el cuerpo de la respuesta no está vacío para admitir el mantenimiento de conexión HTTP 1.0. Por lo tanto, cuando se realiza una solicitud HTTP 1.0 y la directiva lua_http10_buffering está activada, ngx_lua almacenará en búfer la salida de las llamadas a ngx.say y ngx.print y también pospondrá el envío de los encabezados de respuesta hasta que se reciba toda la salida del cuerpo de la respuesta. En ese momento, ngx_lua puede calcular la longitud total del cuerpo y construir un encabezado Content-Length adecuado para devolver al cliente HTTP 1.0. Sin embargo, si el encabezado de respuesta Content-Length se establece en el código Lua en ejecución, este almacenamiento en búfer se desactivará incluso si la directiva lua_http10_buffering está activada.

Para respuestas de salida de transmisión grandes, es importante desactivar la directiva lua_http10_buffering para minimizar el uso de memoria.

Ten en cuenta que herramientas de referencia HTTP comunes como ab y http_load emiten solicitudes HTTP 1.0 por defecto. Para forzar a curl a enviar solicitudes HTTP 1.0, usa la opción -0.

Enlazando Estáticamente Módulos de Lua Pura

Con LuaJIT 2.x, es posible enlazar estáticamente el bytecode de módulos de Lua pura en el ejecutable de Nginx.

Puedes usar el ejecutable luajit para compilar archivos de módulos de Lua .lua a archivos de objeto .o que contienen los datos de bytecode exportados, y luego enlazar los archivos .o directamente en tu compilación de Nginx.

A continuación se muestra un ejemplo trivial para demostrar esto. Considera que tenemos el siguiente archivo .lua llamado foo.lua:

 -- foo.lua
 local _M = {}

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

 return _M

Y luego compilamos este archivo .lua a un archivo foo.o:

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

Lo que importa aquí es el nombre del archivo .lua, que determina cómo usarás este módulo más adelante en el ámbito de Lua. El nombre del archivo foo.o no importa en absoluto excepto por la extensión de archivo .o (que le dice a luajit qué formato de salida se utiliza). Si deseas eliminar la información de depuración de Lua del bytecode resultante, simplemente puedes especificar la opción -b en lugar de -bg.

Luego, al construir Nginx o OpenResty, pasa la opción --with-ld-opt="foo.o" al script ./configure:

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

Finalmente, solo puedes hacer lo siguiente en cualquier código Lua ejecutado por ngx_lua:

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

Y este fragmento de código ya no depende del archivo externo foo.lua porque ya ha sido compilado en el ejecutable de nginx.

Si deseas usar un punto en el nombre del módulo Lua al llamar a require, como en

 local foo = require "resty.foo"

entonces necesitas renombrar el archivo foo.lua a resty_foo.lua antes de compilarlo en un archivo .o con la utilidad de línea de comandos luajit.

Es importante usar exactamente la misma versión de LuaJIT al compilar archivos .lua a archivos .o que al construir nginx + ngx_lua. Esto se debe a que el formato de bytecode de LuaJIT puede ser incompatible entre diferentes versiones de LuaJIT. Cuando el formato de bytecode es incompatible, verás un error de tiempo de ejecución de Lua que dice que el módulo Lua no se encuentra.

Cuando tengas múltiples archivos .lua para compilar y enlazar, simplemente especifica sus archivos .o al mismo tiempo en el valor de la opción --with-ld-opt. Por ejemplo,

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

Si tienes demasiados archivos .o, entonces puede que no sea factible nombrarlos todos en un solo comando. En este caso, puedes construir una biblioteca estática (o archivo de archivo) para tus archivos .o, como en

 ar rcus libmyluafiles.a *.o

luego puedes enlazar el archivo myluafiles como un todo a tu ejecutable de nginx:

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

donde /path/to/lib es la ruta del directorio que contiene el archivo libmyluafiles.a. Cabe señalar que la opción del enlazador --whole-archive es necesaria aquí porque de lo contrario nuestro archivo se omitirá porque no se mencionan símbolos en nuestro archivo en las partes principales del ejecutable de nginx.

Compartición de Datos dentro de un Trabajador de Nginx

Para compartir datos globalmente entre todas las solicitudes manejadas por el mismo proceso de trabajo de Nginx, encapsula los datos compartidos en un módulo de Lua, usa la función incorporada de Lua require para importar el módulo y luego manipula los datos compartidos en Lua. Esto funciona porque los módulos de Lua requeridos se cargan solo una vez y todas las corutinas compartirán la misma copia del módulo (tanto su código como sus datos).

Ten en cuenta que el uso de variables globales de Lua es fuertemente desaconsejado, ya que puede llevar a condiciones de carrera inesperadas entre solicitudes concurrentes.

Aquí hay un pequeño ejemplo sobre cómo compartir datos dentro de un trabajador de Nginx a través de un módulo de Lua:

 -- mydata.lua
 local _M = {}

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

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

 return _M

y luego accediendo desde nginx.conf:

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

El módulo mydata en este ejemplo solo se cargará y ejecutará en la primera solicitud a la ubicación /lua, y todas las solicitudes subsiguientes al mismo proceso de trabajo de Nginx usarán la instancia recargada del módulo así como la misma copia de los datos en él, hasta que se envíe una señal HUP al proceso maestro de Nginx para forzar una recarga. Esta técnica de compartición de datos es esencial para aplicaciones Lua de alto rendimiento basadas en este módulo.

Ten en cuenta que esta compartición de datos es a nivel por trabajador y no a nivel por servidor. Es decir, cuando hay múltiples procesos de trabajo de Nginx bajo un maestro de Nginx, la compartición de datos no puede cruzar el límite de proceso entre estos trabajadores.

Generalmente se recomienda compartir datos de solo lectura de esta manera. También puedes compartir datos cambiables entre todas las solicitudes concurrentes de cada proceso de trabajo de Nginx siempre que no haya no operaciones de I/O no bloqueantes (incluyendo ngx.sleep) en medio de tus cálculos. Siempre que no devuelvas el control al bucle de eventos de Nginx y al programador de hilos ligeros de ngx_lua (incluso implícitamente), nunca habrá condiciones de carrera entre ellos. Por esta razón, siempre ten mucho cuidado cuando desees compartir datos cambiables a nivel de trabajador. Las optimizaciones defectuosas pueden llevar fácilmente a condiciones de carrera difíciles de depurar bajo carga.

Si se requiere la compartición de datos a nivel de servidor, entonces usa uno o más de los siguientes enfoques:

  1. Usa la API ngx.shared.DICT proporcionada por este módulo.
  2. Usa solo un único trabajador de Nginx y un único servidor (esto no se recomienda cuando hay una CPU multinúcleo o múltiples CPUs en una sola máquina).
  3. Usa mecanismos de almacenamiento de datos como memcached, redis, MySQL o PostgreSQL. Las versiones oficiales de OpenResty vienen con un conjunto de módulos de Nginx y bibliotecas de Lua que proporcionan interfaces con estos mecanismos de almacenamiento de datos.

Problemas Conocidos

Problemas de operación de conexión de socket TCP

El método tcpsock:connect puede indicar success a pesar de fallos de conexión como errores de Connection Refused.

Sin embargo, los intentos posteriores de manipular el objeto cosocket fallarán y devolverán el mensaje de estado de error real generado por la operación de conexión fallida.

Este problema se debe a limitaciones en el modelo de eventos de Nginx y solo parece afectar a Mac OS X.

Yielding/Resuming de Corutinas Lua

  • Debido a que las funciones incorporadas dofile y require de Lua están actualmente implementadas como funciones C en LuaJIT 2.0/2.1, si el archivo Lua que se carga mediante dofile o require invoca ngx.location.capture*, ngx.exec, ngx.exit, u otras funciones de API que requieren yielding en el ámbito de nivel superior del archivo Lua, entonces se generará el error de Lua "attempt to yield across C-call boundary". Para evitar esto, coloca estas llamadas que requieren yielding en tus propias funciones Lua en el archivo Lua en lugar de en el ámbito de nivel superior del archivo.

Alcance de Variables Lua

Se debe tener cuidado al importar módulos, y se debe utilizar esta forma:

 local xxx = require('xxx')

en lugar de la antigua forma obsoleta:

 require('xxx')

Aquí está la razón: por diseño, el entorno global tiene exactamente la misma duración que el controlador de solicitud de Nginx asociado. Cada controlador de solicitud tiene su propio conjunto de variables globales de Lua y esa es la idea de aislamiento de solicitudes. El módulo de Lua se carga realmente por el primer controlador de solicitud de Nginx y se almacena en caché por la función incorporada require() en la tabla package.loaded para referencia posterior, y la función module() utilizada por algunos módulos de Lua tiene el efecto secundario de establecer una variable global en la tabla del módulo cargado. Pero esta variable global se borrará al final del controlador de solicitud, y cada controlador de solicitud subsiguiente tendrá su propio entorno global (limpio). Por lo tanto, se obtendrá una excepción de Lua al acceder al valor nil.

El uso de variables globales de Lua es generalmente desaconsejado en el contexto de ngx_lua ya que:

  1. el mal uso de las variables globales de Lua tiene efectos secundarios perjudiciales en solicitudes concurrentes cuando tales variables deberían ser locales en su ámbito,
  2. las variables globales de Lua requieren búsquedas de tablas Lua en el entorno global, lo cual es computacionalmente costoso, y
  3. algunas referencias a variables globales de Lua pueden incluir errores de tipeo que hacen que sean difíciles de depurar.

Por lo tanto, se recomienda encarecidamente declarar siempre tales dentro de un ámbito local apropiado.

 -- Evitar
 foo = 123
 -- Recomendado
 local foo = 123

 -- Evitar
 function foo() return 123 end
 -- Recomendado
 local function foo() return 123 end

Para encontrar todas las instancias de variables globales de Lua en tu código Lua, ejecuta la herramienta lua-releng en todos los archivos fuente .lua:

$ lua-releng
Checking use of Lua global variables in file lib/foo/bar.lua ...
        1       [1489]  SETGLOBAL       7 -1    ; contains
        55      [1506]  GETGLOBAL       7 -3    ; setvar
        3       [1545]  GETGLOBAL       3 -4    ; varexpand

La salida dice que la línea 1489 del archivo lib/foo/bar.lua escribe en una variable global llamada contains, la línea 1506 lee de la variable global setvar, y la línea 1545 lee la variable global varexpand.

Esta herramienta garantizará que las variables locales en las funciones del módulo de Lua estén todas declaradas con la palabra clave local, de lo contrario se lanzará una excepción en tiempo de ejecución. Previene condiciones de carrera indeseables al acceder a tales variables. Consulta Compartición de Datos dentro de un Trabajador de Nginx para conocer las razones detrás de esto.

Ubicaciones Configuradas por Directivas de Subsolicitud de Otros Módulos

Las directivas ngx.location.capture y ngx.location.capture_multi no pueden capturar ubicaciones que incluyan las directivas add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest, o echo_subrequest_async.

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

no funcionará como se espera.

Cosockets No Disponibles en Todas Partes

Debido a limitaciones internas en el núcleo de Nginx, la API de cosocket está deshabilitada en los siguientes contextos: set_by_lua*, log_by_lua*, header_filter_by_lua*, y body_filter_by_lua.

Los cosockets también están actualmente deshabilitados en los contextos de init_by_lua* y init_worker_by_lua* pero podemos agregar soporte para estos contextos en el futuro porque no hay limitación en el núcleo de Nginx (o la limitación podría ser superada).

Sin embargo, existe una solución alternativa cuando el contexto original no necesita esperar los resultados del cosocket. Es decir, crear un temporizador de cero retraso a través de la API ngx.timer.at y realizar las operaciones de cosocket en el controlador del temporizador, que se ejecuta de manera asíncrona en relación con el contexto original que crea el temporizador.

Secuencias de Escape Especiales

NOTA A partir de la versión v0.9.17, este problema se puede evitar utilizando las directivas de configuración *_by_lua_block {}.

Las secuencias PCRE como \d, \s o \w, requieren atención especial porque en literales de cadena, el carácter de barra invertida, \, se elimina tanto por el analizador de lenguaje de Lua como por el analizador del archivo de configuración de Nginx antes de procesarse si no está dentro de una directiva *_by_lua_block {}. Así que el siguiente fragmento no funcionará como se espera:

 # nginx.conf
 ? location /test {
 ?     content_by_lua '
 ?         local regex = "\d+"  -- ESTO ES INCORRECTO FUERA DE UNA DIRECTIVA *_by_lua_block
 ?         local m = ngx.re.match("hello, 1234", regex)
 ?         if m then ngx.say(m[0]) else ngx.say("not matched!") end
 ?     ';
 ? }
 # evalúa a "not matched!"

Para evitar esto, escapa el carácter de barra invertida dos veces:

 # 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("not matched!") end
     ';
 }
 # evalúa a "1234"

Aquí, \\\\d+ se reduce a \\d+ por el analizador del archivo de configuración de Nginx y esto se reduce aún más a \d+ por el analizador de lenguaje de Lua antes de ejecutarse.

Alternativamente, el patrón regex puede presentarse como un literal de cadena de Lua entre corchetes largos, [[...]], en cuyo caso los caracteres de barra invertida solo deben escaparse una vez para el analizador del archivo de configuración de 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("not matched!") end
     ';
 }
 # evalúa a "1234"

Aquí, [[\\d+]] se reduce a [[\d+]] por el analizador del archivo de configuración de Nginx y esto se procesa correctamente.

Ten en cuenta que puede ser necesaria una forma más larga de los corchetes largos, [=[...]=], si el patrón regex contiene secuencias [...]. La forma [=[...]=] puede usarse como la forma predeterminada si se desea.

 # 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("not matched!") end
     ';
 }
 # evalúa a "1234"

Un enfoque alternativo para escapar secuencias PCRE es asegurarse de que el código Lua se coloque en archivos de script externos y se ejecute utilizando las diversas directivas *_by_lua_file. Con este enfoque, las barras invertidas solo se eliminan por el analizador de lenguaje de Lua y, por lo tanto, solo necesitan escaparse una vez cada una.

 -- test.lua
 local regex = "\\d+"
 local m = ngx.re.match("hello, 1234", regex)
 if m then ngx.say(m[0]) else ngx.say("not matched!") end
 -- evalúa a "1234"

Dentro de archivos de script externos, las secuencias PCRE presentadas como literales de cadena de Lua entre corchetes largos no requieren modificación.

 -- test.lua
 local regex = [[\d+]]
 local m = ngx.re.match("hello, 1234", regex)
 if m then ngx.say(m[0]) else ngx.say("not matched!") end
 -- evalúa a "1234"

Como se mencionó anteriormente, las secuencias PCRE presentadas dentro de directivas *_by_lua_block {} (disponibles después de la versión v0.9.17) no requieren modificación.

 # 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("not matched!") end
     }
 }
 # evalúa a "1234"

NOTA Se recomienda usar by_lua_file cuando el código Lua es muy largo.

Mezclar con SSI No Soportado

Mezclar SSI con ngx_lua en la misma solicitud de Nginx no es compatible en absoluto. Simplemente usa ngx_lua exclusivamente. Todo lo que puedes hacer con SSI se puede hacer sobre ngx_lua de todos modos y puede ser más eficiente al usar ngx_lua.

Modo SPDY No Totalmente Soportado

Ciertas APIs de Lua proporcionadas por ngx_lua no funcionan en el modo SPDY de Nginx: ngx.location.capture, ngx.location.capture_multi, y ngx.req.socket.

Datos Faltantes en Solicitudes Cortocircuitadas

Nginx puede terminar una solicitud temprano con (al menos):

  • 400 (Solicitud Incorrecta)
  • 405 (No Permitido)
  • 408 (Tiempo de Espera de Solicitud)
  • 413 (Entidad de Solicitud Demasiado Grande)
  • 414 (URI de Solicitud Demasiado Grande)
  • 494 (Encabezados de Solicitud Demasiado Grandes)
  • 499 (Cliente Cerró la Solicitud)
  • 500 (Error Interno del Servidor)
  • 501 (No Implementado)

Esto significa que las fases que normalmente se ejecutan se omiten, como la fase de reescritura o de acceso. Esto también significa que las fases posteriores que se ejecutan independientemente, por ejemplo, log_by_lua, no tendrán acceso a la información que normalmente se establece en esas fases.

Cambios

Los cambios realizados en cada lanzamiento de este módulo se enumeran en los registros de cambios del paquete OpenResty:

https://openresty.org/#Changes

Ver También

Publicaciones de blog:

Otros módulos y bibliotecas relacionadas:

Directivas

Los bloques básicos de construcción de scripting en Nginx con Lua son las directivas. Las directivas se utilizan para especificar cuándo se ejecuta el código Lua del usuario y cómo se utilizará el resultado. A continuación se muestra un diagrama que muestra el orden en el que se ejecutan las directivas.

Directivas de Módulos Lua Nginx

lua_load_resty_core

syntax: lua_load_resty_core on|off

default: lua_load_resty_core on

context: http

Esta directiva está obsoleta desde el lanzamiento v0.10.16 de este módulo. El módulo resty.core de lua-resty-core ahora se carga obligatoriamente durante la inicialización de la VM de Lua. Especificar esta directiva no tendrá efecto.

Esta directiva se introdujo por primera vez en el lanzamiento v0.10.15 y se utilizaba para cargar opcionalmente el módulo resty.core.

Volver al TOC

lua_capture_error_log

syntax: lua_capture_error_log size

default: none

context: http

Habilita un búfer del tamaño especificado para capturar todos los datos de mensajes del registro de errores de Nginx (no solo aquellos producidos por este módulo o el subsistema http de Nginx, sino todo) sin tocar archivos o discos.

Puedes usar unidades como k y m en el valor de size, como en

 lua_capture_error_log 100k;

Como regla general, un búfer de 4KB puede contener aproximadamente 20 mensajes de registro de errores típicos. ¡Así que haz las cuentas!

Este búfer nunca crece. Si está lleno, nuevos mensajes de registro de errores reemplazarán a los más antiguos en el búfer.

El tamaño del búfer debe ser mayor que la longitud máxima de un solo mensaje de registro de errores (que es de 4K en OpenResty y 2K en NGINX estándar).

Puedes leer los mensajes en el búfer en el espacio Lua a través de la función get_logs() del módulo ngx.errlog de la biblioteca lua-resty-core. Esta función de API de Lua devolverá los mensajes de registro de errores capturados y también eliminará estos ya leídos del búfer de captura global, haciendo espacio para cualquier nuevo dato de registro de errores. Por esta razón, el usuario no debe configurar este búfer para que sea demasiado grande si el usuario lee los datos de registro de errores almacenados en búfer lo suficientemente rápido.

Ten en cuenta que el nivel de registro especificado en la directiva estándar error_log tiene efecto en esta instalación de captura. Solo captura mensajes de registro de un nivel no inferior al nivel de registro especificado en la directiva error_log. El usuario aún puede elegir establecer un nivel de filtrado de registro aún más alto sobre la marcha a través de la función de API de Lua errlog.set_filter_level. Así que es más flexible que la estática error_log.

Vale la pena señalar que no hay forma de capturar los registros de depuración sin construir OpenResty o Nginx con la opción ./configure --with-debug. Y habilitar los registros de depuración se desaconseja en compilaciones de producción debido a la alta sobrecarga.

Esta directiva se introdujo por primera vez en el lanzamiento v0.10.9.

Volver al TOC

lua_use_default_type

syntax: lua_use_default_type on | off

default: lua_use_default_type on

context: http, server, location, location if

Especifica si se debe usar el tipo MIME especificado por la directiva default_type para el valor predeterminado del encabezado de respuesta Content-Type. Desactiva esta directiva si no se desea un encabezado de respuesta Content-Type predeterminado para los manejadores de solicitudes de Lua.

Esta directiva está activada por defecto.

Esta directiva se introdujo por primera vez en el lanzamiento v0.9.1.

Volver al TOC

lua_malloc_trim

syntax: lua_malloc_trim

default: lua_malloc_trim 1000

context: http

Pide a la biblioteca de tiempo de ejecución libc subyacente que libere su memoria libre en caché de vuelta al sistema operativo cada N solicitudes procesadas por el núcleo de Nginx. Por defecto, N es 1000. Puedes configurar el conteo de solicitudes utilizando tus propios números. Números más pequeños significan liberaciones más frecuentes, lo que puede introducir un mayor consumo de tiempo de CPU y una huella de memoria más pequeña, mientras que números más grandes generalmente conducen a menos sobrecarga de tiempo de CPU y una huella de memoria relativamente más grande. Simplemente ajusta el número para tus propios casos de uso.

Configurar el argumento a 0 desactiva esencialmente el recorte de memoria periódico por completo.

 lua_malloc_trim 0;  # desactivar recorte completamente

La implementación actual utiliza un controlador de fase de registro de Nginx para contar las solicitudes. Por lo tanto, la aparición de las directivas log_subrequest on en nginx.conf puede hacer que el conteo sea más rápido cuando se involucran subsolicitudes. Por defecto, solo se cuentan las "solicitudes principales".

Ten en cuenta que esta directiva no afecta la memoria asignada por el propio asignador de LuaJIT basado en la llamada al sistema mmap.

Esta directiva se introdujo por primera vez en el lanzamiento v0.10.7.

Volver al TOC

lua_code_cache

syntax: lua_code_cache on | off

default: lua_code_cache on

context: http, server, location, location if

Habilita o deshabilita la caché de código Lua para el código Lua en las directivas *_by_lua_file (como set_by_lua_file y content_by_lua_file) y módulos de Lua.

Al desactivarlo, cada solicitud atendida por ngx_lua se ejecutará en una instancia separada de VM de Lua, comenzando desde el lanzamiento 0.9.3. Así que los archivos Lua referenciados en set_by_lua_file, content_by_lua_file, access_by_lua_file, etc. no se almacenarán en caché y todos los módulos de Lua utilizados se cargarán desde cero. Con esto, los desarrolladores pueden adoptar un enfoque de edición y actualización.

Ten en cuenta, sin embargo, que el código Lua escrito en línea dentro de nginx.conf, como aquellos especificados por set_by_lua, content_by_lua, access_by_lua, y rewrite_by_lua no se actualizará cuando edites el código Lua en línea en tu archivo nginx.conf porque solo el analizador del archivo de configuración de Nginx puede analizar correctamente el archivo nginx.conf y la única forma es recargar el archivo de configuración enviando una señal HUP o simplemente reiniciar Nginx.

Incluso cuando la caché de código está habilitada, los archivos Lua que se cargan mediante dofile o loadfile en *_by_lua_file no pueden ser almacenados en caché (a menos que almacenes los resultados tú mismo). Normalmente puedes usar las directivas init_by_lua o init_by_lua_file para cargar todos esos archivos o simplemente hacer que estos archivos Lua sean módulos de Lua verdaderos y cargarlos a través de require.

El módulo ngx_lua no admite el modo stat disponible con el módulo Apache mod_lua (aún).

Deshabilitar la caché de código Lua se desaconseja fuertemente para uso en producción y solo debe usarse durante el desarrollo, ya que tiene un impacto negativo significativo en el rendimiento general. Por ejemplo, el rendimiento de un ejemplo de Lua "hello world" puede caer en un orden de magnitud después de deshabilitar la caché de código Lua.

Volver al TOC

lua_thread_cache_max_entries

syntax: lua_thread_cache_max_entries

default: lua_thread_cache_max_entries 1024

context: http

Especifica el número máximo de entradas permitidas en la caché de objetos de hilo de Lua a nivel de proceso de trabajo.

Esta caché recicla los objetos GC de hilo de Lua entre todos nuestros "hilos ligeros".

Un valor cero de <num> desactiva la caché.

Ten en cuenta que esta función requiere LuaJIT de OpenResty con la nueva API C lua_resetthread.

Esta función se introdujo por primera vez en la versión v0.10.9.

Volver al TOC

lua_regex_cache_max_entries

syntax: lua_regex_cache_max_entries

default: lua_regex_cache_max_entries 1024

context: http

Especifica el número máximo de entradas permitidas en la caché de regex compilados a nivel de proceso de trabajo.

Las expresiones regulares utilizadas en ngx.re.match, ngx.re.gmatch, ngx.re.sub, y ngx.re.gsub se almacenarán en caché dentro de esta caché si se especifica la opción regex o (es decir, la bandera de compilar una vez).

El número predeterminado de entradas permitidas es 1024 y cuando se alcanza este límite, nuevas expresiones regulares no se almacenarán en caché (como si no se hubiera especificado la opción o) y habrá una advertencia en el archivo error.log:

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

Si estás utilizando la implementación ngx.re.* de lua-resty-core cargando el módulo resty.core.regex (o solo el módulo resty.core), entonces se utiliza una caché LRU para la caché de regex que se utiliza aquí.

No actives la opción o para expresiones regulares (y/o argumentos de cadena replace para ngx.re.sub y ngx.re.gsub) que se generan sobre la marcha y dan lugar a variaciones infinitas para evitar alcanzar el límite especificado.

Volver al TOC

lua_regex_match_limit

syntax: lua_regex_match_limit

default: lua_regex_match_limit 0

context: http

Especifica el "límite de coincidencia" utilizado por la biblioteca PCRE al ejecutar la API ngx.re. Para citar la página de manual de PCRE, "el límite ... tiene el efecto de limitar la cantidad de retrocesos que pueden ocurrir".

Cuando se alcanza el límite, la cadena de error "pcre_exec() failed: -8" será devuelta por las funciones de la API ngx.re.

Al establecer el límite en 0, se utiliza el "límite de coincidencia" predeterminado al compilar la biblioteca PCRE. Y este es el valor predeterminado de esta directiva.

Esta directiva se introdujo por primera vez en el lanzamiento v0.8.5.

Volver al TOC

lua_package_path

syntax: lua_package_path

default: El contenido de la variable de entorno LUA_PATH o los valores predeterminados compilados de Lua.

context: http

Establece la ruta de búsqueda de módulos de Lua utilizada por scripts especificados por set_by_lua, content_by_lua y otros. La cadena de ruta está en la forma de ruta estándar de Lua, y ;; puede usarse para representar las rutas de búsqueda originales.

A partir del lanzamiento v0.5.0rc29, la notación especial $prefix o ${prefix} puede usarse en la cadena de ruta de búsqueda para indicar la ruta del prefijo del servidor normalmente determinada por la opción de línea de comandos -p PATH al iniciar el servidor Nginx.

Volver al TOC

lua_package_cpath

syntax: lua_package_cpath

default: El contenido de la variable de entorno LUA_CPATH o los valores predeterminados compilados de Lua.

context: http

Establece la ruta de búsqueda de módulos C de Lua utilizada por scripts especificados por set_by_lua, content_by_lua y otros. La cadena cpath está en la forma estándar de cpath de Lua, y ;; puede usarse para representar el cpath original.

A partir del lanzamiento v0.5.0rc29, la notación especial $prefix o ${prefix} puede usarse en la cadena de ruta de búsqueda para indicar la ruta del prefijo del servidor normalmente determinada por la opción de línea de comandos -p PATH al iniciar el servidor Nginx.

Volver al TOC

init_by_lua

syntax: init_by_lua

context: http

phase: loading-config

NOTA El uso de esta directiva es desaconsejado después del lanzamiento v0.9.17. Usa la directiva init_by_lua_block en su lugar.

Similar a la directiva init_by_lua_block, pero acepta el código fuente de Lua directamente en un literal de cadena de Nginx (lo que requiere una escapatoria de caracteres especial).

Por ejemplo,

 init_by_lua '
     print("I need no extra escaping here, for example: \r\nblah")
 '

Esta directiva se introdujo por primera vez en el lanzamiento v0.5.5.

Volver al TOC

init_by_lua_block

syntax: init_by_lua_block { lua-script }

context: http

phase: loading-config

Cuando Nginx recibe la señal HUP y comienza a recargar el archivo de configuración, la VM de Lua también se recreará y init_by_lua_block se ejecutará nuevamente en la nueva VM de Lua. En caso de que la directiva lua_code_cache esté desactivada (activada por defecto), el controlador init_by_lua_block se ejecutará en cada solicitud porque en este modo especial siempre se crea una VM de Lua independiente para cada solicitud.

Normalmente puedes precargar módulos de Lua al inicio del servidor mediante este gancho y aprovechar la optimización de copia en escritura (COW) de los sistemas operativos modernos. Aquí hay un ejemplo para precargar módulos de Lua:

 # esto se ejecuta antes de bifurcar procesos de trabajo de nginx:
 init_by_lua_block { require "cjson" }

 server {
     location = /api {
         content_by_lua_block {
             -- el siguiente require() solo devolverá
             -- el módulo ya cargado de package.loaded:
             ngx.say(require "cjson".encode{dog = 5, cat = 6})
         }
     }
 }

También puedes inicializar el almacenamiento shm lua_shared_dict en esta fase. Aquí hay un ejemplo para esto:

 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"))
         }
     }
 }

Pero ten en cuenta que el almacenamiento shm de lua_shared_dict no se borrará a través de una recarga de configuración (por ejemplo, mediante la señal HUP). Así que si no deseas reinicializar el almacenamiento shm en tu código de init_by_lua_block en este caso, solo necesitas establecer una bandera personalizada en el almacenamiento shm y siempre verificar la bandera en tu código de init_by_lua_block.

Debido a que el código Lua en este contexto se ejecuta antes de que Nginx bifurque sus procesos de trabajo (si los hay), los datos o el código cargados aquí disfrutarán de la característica de Copia en escritura (COW) proporcionada por muchos sistemas operativos entre todos los procesos de trabajo, ahorrando así mucha memoria.

No inicialices tus propias variables globales de Lua en este contexto porque el uso de variables globales de Lua tiene penalizaciones de rendimiento y puede llevar a la contaminación del espacio de nombres global (ver la sección Alcance de Variables Lua para más detalles). La forma recomendada es usar archivos de módulo de Lua adecuados (pero no uses la función estándar de Lua module() para definir módulos de Lua porque también contamina el espacio de nombres global) y llamar a require() para cargar tus propios archivos de módulo en init_by_lua_block u otros contextos (require() almacena en caché los módulos de Lua cargados en la tabla global package.loaded en el registro de Lua, por lo que tus módulos solo se cargarán una vez para toda la instancia de VM de Lua).

Solo un pequeño conjunto de la API de Nginx para Lua es compatible en este contexto:

Más APIs de Nginx para Lua pueden ser compatibles en este contexto a petición futura de los usuarios.

Básicamente, puedes usar de forma segura bibliotecas de Lua que realicen I/O bloqueante en este contexto porque bloquear el proceso maestro durante el inicio del servidor está completamente bien. Incluso el núcleo de Nginx realiza I/O bloqueante (al menos al resolver los nombres de host upstream) en la fase de carga de configuración.

Debes tener mucho cuidado con las posibles vulnerabilidades de seguridad en tu código Lua registrado en este contexto porque el proceso maestro de Nginx a menudo se ejecuta bajo la cuenta root.

Esta directiva se introdujo por primera vez en el lanzamiento v0.9.17.

Consulta también las siguientes publicaciones de blog para más detalles sobre las zonas de memoria compartida de OpenResty y Nginx:

Volver al TOC

init_by_lua_file

syntax: init_by_lua_file

context: http

phase: loading-config

Equivalente a init_by_lua_block, excepto que el archivo especificado por <path-to-lua-script-file> contiene el código Lua o bytecode de LuaJIT que se ejecutará.

Cuando se da una ruta relativa como foo/bar.lua, se convertirá en la ruta absoluta relativa a la ruta del prefijo del servidor determinada por la opción de línea de comandos -p PATH al iniciar el servidor Nginx.

Esta directiva se introdujo por primera vez en el lanzamiento v0.5.5.

Volver al TOC

init_worker_by_lua

syntax: init_worker_by_lua

context: http

phase: starting-worker

NOTA El uso de esta directiva es desaconsejado después del lanzamiento v0.9.17. Usa la directiva init_worker_by_lua_block en su lugar.

Similar a la directiva init_worker_by_lua_block, pero acepta el código fuente de Lua directamente en un literal de cadena de Nginx (lo que requiere una escapatoria de caracteres especial).

Por ejemplo,

 init_worker_by_lua '
     print("I need no extra escaping here, for example: \r\nblah")
 ';

Esta directiva se introdujo por primera vez en el lanzamiento v0.9.5.

Volver al TOC

init_worker_by_lua_block

syntax: init_worker_by_lua_block { lua-script }

context: http

phase: starting-worker

Ejecuta el código Lua especificado en cada inicio de proceso de trabajo de Nginx cuando el proceso maestro está habilitado. Cuando el proceso maestro está deshabilitado, este gancho solo se ejecutará después de init_by_lua*.

Este gancho se utiliza a menudo para crear temporizadores recurrentes por trabajador (a través de la API de Lua ngx.timer.at), ya sea para verificación de salud de backend u otro trabajo rutinario programado. A continuación se muestra un ejemplo:

```nginx

init_worker_by_lua_block { local delay = 3 -- en segundos local new_timer = ngx.timer.at local log = ngx.log local ERR = ngx.ERR local check

 check = function(premature)
     if not premature then
         -- realizar la verificación de salud u otro trabajo rutinario
         local ok, err = new_timer(delay, check)
         if not ok then
             log(ERR, "failed to create timer: ", err)
             return
         end
     end

     -- hacer algo en el temporizador
 end

 local