lua: Поддержка скриптов Lua для NGINX
Установка для Debian/Ubuntu
Эти документы применимы к пакету APT nginx-module-lua, предоставляемому репозиторием GetPageSpeed Extras.
- Настройте репозиторий APT, как описано в настройке репозитория APT.
- Установите модуль:
sudo apt-get update
sudo apt-get install nginx-module-lua
Показать выпуски и архитектуры
| Дистрибутив | Версия | Компонент | Архитектуры |
|-------------|-------------------|-------------|-----------------|
| debian | bookworm | main | amd64, arm64 |
| debian | bookworm-mainline | main | amd64, arm64 |
| debian | trixie | main | amd64, arm64 |
| debian | trixie-mainline | main | amd64, arm64 |
| ubuntu | focal | main | amd64, arm64 |
| ubuntu | focal-mainline | main | amd64, arm64 |
| ubuntu | jammy | main | amd64, arm64 |
| ubuntu | jammy-mainline | main | amd64, arm64 |
| ubuntu | noble | main | amd64, arm64 |
| ubuntu | noble-mainline | main | amd64, arm64 |
ngx_http_lua_module - Встраивание мощности Lua в HTTP-серверы Nginx.
Этот модуль является основным компонентом OpenResty. Если вы используете этот модуль, то по сути вы используете OpenResty :)
Статус
Готов к производству.
Видео
-
Видео на YouTube "Пример Hello World HTTP с OpenResty/Lua"
-
Видео на YouTube "Напишите свои собственные модули Lua в приложениях OpenResty/Nginx"
-
Видео на YouTube "Демонстрация утилиты командной строки OpenResty resty"
-
Видео на YouTube "Правильное измерение времени выполнения кода Lua в OpenResty"
-
Видео на YouTube "Предварительная компиляция модулей Lua в байт-код LuaJIT для ускорения запуска OpenResty"
Приглашаем вас подписаться на наш официальный канал YouTube, OpenResty.
Аннотация
# устанавливаем пути поиска для чистых библиотек Lua (';;' - путь по умолчанию):
# устанавливаем пути поиска для библиотек Lua, написанных на C (можно использовать ';;'):
server {
location /lua_content {
# MIME-тип определяется параметром default_type:
default_type 'text/plain';
content_by_lua_block {
ngx.say('Hello,world!')
}
}
location /nginx_var {
# MIME-тип определяется параметром default_type:
default_type 'text/plain';
# пробуем получить доступ к /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() -- явно читаем тело запроса
local data = ngx.req.get_body_data()
if data then
ngx.say("body data:")
ngx.print(data)
return
end
-- тело может содержать буфер в временном файле:
local file = ngx.req.get_body_file()
if file then
ngx.say("тело в файле ", file)
else
ngx.say("тело не найдено")
end
}
}
# прозрачный неблокирующий ввод-вывод в Lua через подзапросы
# (лучше использовать косоночные сокеты)
location = /lua {
# MIME-тип определяется параметром default_type:
default_type 'text/plain';
content_by_lua_block {
local res = ngx.location.capture("/some_other_location")
if res then
ngx.say("статус: ", res.status)
ngx.say("тело:")
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;
}
# используем переменную nginx в коде
# ВНИМАНИЕ: содержимое в переменной nginx должно быть тщательно отфильтровано,
# в противном случае это приведет к большой угрозе безопасности!
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 {
-- проверяем, находится ли IP-адрес клиента в нашем черном списке
if ngx.var.remote_addr == "132.5.72.3" then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- проверяем, содержит ли URI плохие слова
if ngx.var.uri and
string.match(ngx.var.request_body, "evil")
then
return ngx.redirect("/terms_of_use.html")
end
-- тесты пройдены
}
# настройки proxy_pass/fastcgi_pass и т.д.
}
}
Описание
Этот модуль встраивает LuaJIT 2.0/2.1 в Nginx. Он является основным компонентом OpenResty. Если вы используете этот модуль, то по сути вы используете OpenResty.
С версии v0.10.16 этого модуля стандартный интерпретатор Lua
(также известный как "PUC-Rio Lua") больше не поддерживается.
В этом документе можно использовать термины "Lua" и "LuaJIT" взаимозаменяемо для обозначения интерпретатора LuaJIT.
За счет использования подзапросов Nginx этот модуль позволяет интегрировать мощные потоки Lua (известные как "корутины" Lua) в модель событий Nginx.
В отличие от mod_lua в Apache и mod_magnet в Lighttpd, код Lua, выполняемый с помощью этого модуля, может быть на 100% неблокирующим по сетевому трафику, если используется API Nginx для Lua, предоставляемый этим модулем для обработки запросов к сервисам, таким как MySQL, PostgreSQL, Memcached, Redis или удаленные HTTP веб-сервисы.
По меньшей мере можно использовать следующие библиотеки Lua и модули Nginx с этим модулем:
- 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
Практически любые модули Nginx могут быть использованы с этим модулем ngx_lua с помощью
ngx.location.capture или
ngx.location.capture_multi, но рекомендуется использовать эти библиотеки lua-resty-* вместо создания
подзапросов для доступа к upstream модулям Nginx, так как последние обычно
гораздо более гибкие и экономные по памяти.
Интерпретатор Lua (также известный как "Lua State" или "экземпляр виртуальной машины LuaJIT") поделён между всеми запросами в одном процессе рабочего Nginx, чтобы минимизировать использование памяти. Контексты запросов разделяются с помощью легковесных корутин Lua.
Загруженные модули Lua сохраняются на уровне рабочего процесса Nginx, что приводит к небольшому объему памяти в Lua даже при сильной нагрузке.
Этот модуль подключен к "http" подсистеме Nginx, поэтому он может говорить только по протоколам связи в семейство HTTP (HTTP 0.9/1.0/1.1/2.0, WebSockets и т.д.). Если вы хотите осуществлять общее TCP-сообщение с клиентами на осевом уровне, то вам стоит использовать модуль ngx_stream_lua, который предлагает совместимый Lua API.
Обычные использования
Просто назовем несколько:
- Смешивание и обработка выходных данных различных upstream Nginx (proxy, drizzle, postgres, redis, memcached и т. д.) в Lua,
- выполнение произвольно сложных проверок доступа и безопасности в Lua до того, как запросы фактически достигнут удаленных бэкендов,
- манипулирование заголовками ответов произвольным способом (с помощью Lua)
- получение информации о бэкенде из внешних хранилищ (таких как redis, memcached, mysql, postgresql) и использование этой информации для выбора, к какому upstream бэкенду обращаться на лету,
- создание произвольно сложных веб-приложений в обработчике контента с синхронным, но все еще неблокирующим доступом к бэкендам баз данных и другим хранилищам,
- выполнение очень сложной маршрутизации URL в Lua на этапе переписывания,
- использование Lua для реализации расширенного механизма кеширования для подзапросов Nginx и произвольных местоположений.
Возможности не ограничены, так как модуль позволяет соединять вместе различные элементы в Nginx, а также раскрывать мощь языка Lua для пользователей. Модуль предоставляет полную гибкость скриптов, предлагая производительность, сопоставимую с программами на нативном C языке как по времени ЦП, так и по объему памяти благодаря LuaJIT 2.x.
Имплементации других языков программирования обычно испытывают трудности с тем, чтобы соответствовать этому уровню производительности.
Совместимость с Nginx
Последняя версия этого модуля совместима со следующими версиями Nginx:
- 1.29.x (последняя проверенная: 1.29.2)
- 1.27.x (последняя проверенная: 1.27.1)
- 1.25.x (последняя проверенная: 1.25.1)
- 1.21.x (последняя проверенная: 1.21.4)
- 1.19.x (последняя проверенная: 1.19.3)
- 1.17.x (последняя проверенная: 1.17.8)
- 1.15.x (последняя проверенная: 1.15.8)
- 1.14.x
- 1.13.x (последняя проверенная: 1.13.6)
- 1.12.x
- 1.11.x (последняя проверенная: 1.11.2)
- 1.10.x
- 1.9.x (последняя проверенная: 1.9.15)
- 1.8.x
- 1.7.x (последняя проверенная: 1.7.10)
- 1.6.x
Ядра Nginx версий ниже 1.6.0 (исключительно) не поддерживаются.
Репозиторий кода
Репозиторий кода этого проекта размещен на GitHub по адресу openresty/lua-nginx-module.
Поддержка байт-кода LuaJIT
Смотрите видео на YouTube "Правильное измерение времени выполнения кода Lua в OpenResty"
С версии v0.5.0rc32 все директивы конфигурации *_by_lua_file (такие как content_by_lua_file) поддерживают загрузку сырых байт-код файлов LuaJIT 2.0/2.1 напрямую:
/path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc
Опция -bg может быть использована для включения отладочной информации в файл байт-кода LuaJIT:
/path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc
Пожалуйста, обратитесь к официальной документации LuaJIT по опции -b для получения дополнительной информации:
https://luajit.org/running.html#opt_b
Обратите внимание, что файлы байт-кода, сгенерированные LuaJIT 2.1, не совместимы с LuaJIT 2.0 и наоборот. Поддержка байт-кода LuaJIT 2.1 была впервые добавлена в ngx_lua v0.9.3.
Попытки загрузить файлы байт-кода стандартного Lua 5.1 в экземпляры ngx_lua, привязанные к LuaJIT 2.0/2.1 (или наоборот), приведут к сообщению об ошибке Nginx, как в примере ниже:
[error] 13909#0: *1 не удалось загрузить встроенный код Lua: неправильный заголовок байт-кода в /path/to/test_file.luac
Загрузка файлов байт-кода через примитивы Lua, такие как require и
dofile, всегда должна работать так, как ожидалось.
Поддержка переменных окружения системы
Если вы хотите получить доступ к переменной окружения системы, скажем, foo, в Lua через стандартный API Lua os.getenv, то вам также следует указать имя этой переменной окружения в вашем файле nginx.conf через директиву env. Например,
env foo;
Поддержка HTTP 1.0
Протокол HTTP 1.0 не поддерживает изменяемый вывод и требует явного заголовка Content-Length, когда тело ответа не пустое, чтобы поддерживать удержание соединения HTTP 1.0.
Таким образом, когда выполняется запрос HTTP 1.0 и директива lua_http10_buffering включена, ngx_lua будет буферизовать
вывод вызовов ngx.say и ngx.print и также откладывать отправку заголовков ответа, пока не будет получен весь вывод тела ответа.
В это время ngx_lua может вычислить общую длину тела и создать правильный заголовок Content-Length, который будет возвращен клиенту HTTP 1.0.
Если заголовок ответа Content-Length устанавливается в работающем коде Lua, то это буферизирование будет отключено, даже если директива lua_http10_buffering включена.
Для крупных потоковых выходных ответов важно отключить директиву lua_http10_buffering, чтобы минимизировать использование памяти.
Обратите внимание, что такие общие инструменты для бенчмаркинга HTTP, как ab и http_load, по умолчанию выдают запросы HTTP 1.0.
Чтобы принудить curl отправить запросы HTTP 1.0, используйте опцию -0.
Статическая компоновка чистых модулей Lua
С помощью LuaJIT 2.x возможно статически компилировать байт-код чистых модулей Lua в исполняемый файл Nginx.
Вы можете использовать исполняемый файл luajit для компиляции .lua файлов модулей Lua в .o объектные файлы, содержащие экспортированные данные байт-кода, а затем скомпилировать .o файлы непосредственно в вашем сборке Nginx.
Ниже приведен тривиальный пример, чтобы продемонстрировать это. Допустим, у нас есть следующий файл .lua, названный foo.lua:
-- foo.lua
local _M = {}
function _M.go()
print("Hello from foo")
end
return _M
Затем мы компилируем этот .lua файл в файл foo.o:
/path/to/luajit/bin/luajit -bg foo.lua foo.o
Важно, чтобы название файла .lua определяло, как вы будете использовать этот модуль позже в Lua. Название файла foo.o не имеет значения, кроме как расширение .o (которое говорит luajit, какой формат вывода используется). Если вы хотите удалить информацию отладки Lua из результирующего байт-кода, просто укажите опцию -b, вместо -bg.
Затем, при сборке Nginx или OpenResty, передайте параметр --with-ld-opt="foo.o" скрипту ./configure:
./configure --with-ld-opt="/path/to/foo.o" ...
Наконец, вы можете просто сделать следующее в любом коде Lua, исполняемом ngx_lua:
local foo = require "foo"
foo.go()
И этот кусок кода больше не зависит от внешнего файла foo.lua, поскольку он уже скомпилирован в исполняемый файл nginx.
Если вы хотите использовать точку в имени модуля Lua при вызове require, как в
local foo = require "resty.foo"
то вам нужно переименовать файл foo.lua в resty_foo.lua, прежде чем компилировать его в файл .o с помощью утилиты командной строки luajit.
Важно использовать точно такую же версию LuaJIT при компиляции .lua файлов в .o файлы, как и при построении Nginx + ngx_lua. Это происходит потому, что формат байт-кода LuaJIT может быть несовместим между разными версиями LuaJIT. Когда формат байт-кода несовместим, вы получите ошибку выполнения Lua, говорящую о том, что модуль Lua не найден.
Когда у вас есть несколько .lua файлов для компиляции и привязки, просто укажите их .o файлы одновременно в значении параметра --with-ld-opt. Например,
./configure --with-ld-opt="/path/to/foo.o /path/to/bar.o" ...
Если у вас слишком много .o файлов, может оказаться невозможным указать их все в одной команде. В этом случае вы можете создать статическую библиотеку (или архив) для ваших .o файлов, как в следующем примере:
ar rcus libmyluafiles.a *.o
Затем вы можете линковать архив myluafiles целиком к вашему исполняемому файлу nginx:
./configure \
--with-ld-opt="-L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive"
где /path/to/lib это путь к каталогу, содержащему файл libmyluafiles.a. Следует отметить, что параметр соединителя --whole-archive обязателен, иначе наш архив будет пропущен, поскольку ни один символ в нашем архиве не упоминается в основных частях исполняемого файла nginx.
Совместное использование данных внутри рабочего процесса Nginx
Для глобального совместного использования данных среди всех запросов, обрабатываемых одним и тем же рабочим процессом Nginx, инкапсулируйте общие данные в модуль Lua, используйте встроенный Lua require для импорта модуля, а затем манипулируйте общими данными в Lua. Это работает потому, что требуемые модули Lua загружаются только один раз, и все корутины будут разделять одну и ту же копию модуля (как его кода, так и данных).
Обратите внимание, что использование глобальных переменных Lua категорически не рекомендуется, так как это может привести к неожиданным состояниям гонки между параллельными запросами.
Вот небольшой пример совместного использования данных внутри рабочего процесса Nginx через модуль Lua:
-- mydata.lua
local _M = {}
local data = {
dog = 3,
cat = 4,
pig = 5,
}
function _M.get_age(name)
return data[name]
end
return _M
и затем доступ к нему из nginx.conf:
location /lua {
content_by_lua_block {
local mydata = require "mydata"
ngx.say(mydata.get_age("dog"))
}
}
Модуль mydata в этом примере будет загружен и выполнен только в первый раз, когда поступит запрос к местоположению /lua,
и все последующие запросы к одному и тому же процессу рабочего Nginx будут использовать уже загруженный экземпляр
модуля, а также ту же копию данных в нем, пока не будет отправлен сигнал HUP к главному процессу Nginx для принуждения перезагрузки.
Эта техника совместного использования данных имеет важное значение для высокопроизводительных Lua-приложений, основанных на этом модуле.
Обратите внимание, что это совместное использование данных происходит на уровне рабочего процесса, а не на уровне сервера. То есть, когда существует несколько процессов рабочих Nginx под родительским Nginx, совместное использование данных не может проходить через границу процесса между этими рабочими.
Обычно рекомендуется делиться только данными для чтения таким образом. Вы также можете делиться изменяемыми данными среди всех параллельных запросов каждого рабочего процесса Nginx, пока в ваших расчетах нет неблокирующих операций ввода-вывода (включая ngx.sleep). Пока вы не передали контроль обратно в цикл событий Nginx и планировщик легких потоков ngx_lua (даже неявно), между ними не может возникнуть никаких состояний гонки. По этой причине всегда будьте очень осторожны, когда вы хотите совместно использовать изменяемые данные на уровне рабочего процесса. Ошибки оптимизации могут легко привести к трудным для отладки состояниям гонки под нагрузкой.
Если требуется совместное использование данных на уровне сервера, используйте один или несколько из следующих подходов:
- Используйте API ngx.shared.DICT, предоставленный этим модулем.
- Используйте только один рабочий процесс Nginx и один сервер (однако это не рекомендуется, когда есть многоядерный ЦП или несколько ЦП в одной машине).
- Используйте механизмы хранения данных, такие как
memcached,redis,MySQLилиPostgreSQL. Официальные релизы OpenResty поставляются с набором дополнительных модулей Nginx и библиотек Lua, которые обеспечивают интерфейсы с этими механизмами хранения данных.
Известные проблемы
Проблемы с операцией соединения TCP-сокета
Метод tcpsock:connect может указывать на success, несмотря на сбои соединения, такие как ошибки Connection Refused.
Тем не менее, поздние попытки манипулировать объектом cosocket завершатся неудачей и вернут фактическое сообщение об ошибке, сгенерированное неудачной операцией соединения.
Эта проблема возникает из-за ограничений в модели событий Nginx и затрагивает только Mac OS X.
Выдача/возобновление корутин Lua
- Поскольку функции
dofileиrequireв Lua в настоящее время реализованы как функции C в LuaJIT 2.0/2.1, если файл Lua, загружаемый с помощьюdofileилиrequire, вызывает ngx.location.capture*, ngx.exec, ngx.exit или другие функции API, требующие выдачи в верхнем уровне области видимости файла Lua, то возникнет ошибка Lua "попытка выдачи через границу C-вызова". Чтобы избежать этого, поместите эти вызовы, требующие выдачи, в ваши собственные функции Lua в файле Lua, а не в верхнем уровне области видимости файла.
Область переменных Lua
При импорте модулей необходимо быть осторожным, и следует использовать следующую форму:
local xxx = require('xxx')
вместо старой устаревшей формы:
require('xxx')
Вот причина: по умолчанию глобальная среда имеет точно такой же срок жизни, как и обработчик запросов Nginx, связанный с ней. Каждый обработчик запросов имеет свой собственный набор переменных глобальной Lua, и это идея изоляции запросов. Модуль Lua фактически загружается первым обработчиком запросов Nginx и кешируется с помощью встроенной функции require() в таблице package.loaded для дальнейшего обращения, и встроенная функция module() некоторых модулей Lua имеет побочный эффект, устанавливающий глобальную переменную на загруженную таблицу модуля. Однако эта глобальная переменная будет очищена в конце обработчика запроса, и каждый последующий обработчик запроса будет иметь собственную (чистую) глобальную среду. Поэтому при обращении к значению nil будет выдано исключение Lua.
Использование глобальных переменных Lua, как правило, нежелательно в контексте ngx_lua, поскольку:
- неправильное использование глобальных Lua приводит к негативным побочным эффектам на одновременные запросы, когда такие переменные должны быть локальными по своему объему,
- глобальные переменные Lua требуют поиска таблиц Lua в глобальной среде, что является вычислительно затратным, и
- некоторые ссылки на глобальные переменные Lua могут содержать ошибки набора, что затрудняет отладку.
Поэтому категорически рекомендуется всегда объявлять такие переменные внутри соответствующей локальной области видимости.
-- Избегайте
foo = 123
-- Рекомендуется
local foo = 123
-- Избегайте
function foo() return 123 end
-- Рекомендуется
local function foo() return 123 end
Чтобы найти все случаи использования глобальных переменных Lua в вашем коде Lua, запустите инструмент lua-releng на всех исходных файлах .lua:
$ lua-releng
Проверка использования глобальных переменных Lua в файле lib/foo/bar.lua ...
1 [1489] SETGLOBAL 7 -1 ; содержит
55 [1506] GETGLOBAL 7 -3 ; setvar
3 [1545] GETGLOBAL 3 -4 ; varexpand
Вывод сообщает, что строка 1489 файла lib/foo/bar.lua записывает в глобальную переменную с именем contains, строка 1506 считывает из глобальной переменной setvar, а строка 1545 считывает глобальную переменную varexpand.
Этот инструмент гарантирует, что все локальные переменные в функциях Lua модулей объявлены с помощью ключевого слова local, в противном случае будет выброшено исключение во время выполнения. Это предотвратит нежелательные состояния гонки при обращении к таким переменным. Смотрите Совместное использование данных внутри рабочего процесса Nginx для понимания причин.
Конфигурация местоположений с помощью директив других модулей
Директивы ngx.location.capture и ngx.location.capture_multi не могут захватывать местоположения, которые включают в себя add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest, или echo_subrequest_async директивы.
location /foo {
content_by_lua_block {
res = ngx.location.capture("/bar")
}
}
location /bar {
echo_location /blah;
}
location /blah {
echo "Успех!";
}
$ curl -i http://example.com/foo
не будет работать так, как ожидалось.
Косокеты недоступны везде
Из-за внутренних ограничений в ядре Nginx API cosocket отключен в следующих контекстах: set_by_lua*, log_by_lua*, header_filter_by_lua*, и body_filter_by_lua.
Косокеты в настоящее время также отключены в контексте директив init_by_lua* и init_worker_by_lua*, но мы можем добавить поддержку по этим контекстам в будущем, так как в ядре Nginx нет никаких ограничений (или ограничение может быть обойдёно).
Тем не менее, существует обход, когда оригинальный контекст не требует ожидать результаты cosocket. То есть, создание таймера с нулевой задержкой через API ngx.timer.at и выполнение результатов cosocket в обработчике таймера, который работает асинхронно по отношению к оригинальному контексту, созданному таймером.
Специальные экранированные последовательности
ПРИМЕЧАНИЕ После выхода версии v0.9.17 эту проблему можно избежать, используя директивы конфигурации *_by_lua_block {}.
Последовательности PCRE, такие как \d, \s или \w, требуют особого внимания, потому что в строковых литералах символ обратной косой черты \ вырезается как парсером языка Lua, так и парсером файла конфигурации Nginx перед обработкой, если они не находятся в директиве *_by_lua_block {}. Поэтому следующий фрагмент не будет работать так, как ожидалось:
# nginx.conf
? location /test {
? content_by_lua '
? local regex = "\d+" -- ЭТО НЕКОРРЕКТНО ВНЕ ДИРЕКТИВЫ *_by_lua_block
? local m = ngx.re.match("hello, 1234", regex)
? if m then ngx.say(m[0]) else ngx.say("недоступно!") end
? ';
? }
# оценивается как "недоступно!"
Чтобы избежать этого, двойное экранирование обратной косой черты:
# 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("недоступно!") end
';
}
# оценивается как "1234"
Здесь \\\\d+ будет сокращена до \\d+ парсером файла конфигурации Nginx, и эта последовательность будет дополнительно сокращена до \d+ парсером языка Lua перед выполнением.
В качестве альтернативы шаблон может быть представлен как строковой литерал Lua с длинными скобками, заключенным в "длинные скобки", [[...]], в этом случае обратные косые черты должны быть экранированы только один раз для парсера конфигурационного файла 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("недоступно!") end
';
}
# оценивается как "1234"
Здесь [[\\d+]] будет сокращена до [[\d+]] парсером конфигурационного файла Nginx, и это будет обработано корректно.
Обратите внимание, что может потребоваться более длинный формат длинной скобки [=[...]=], если шаблон regex содержит последовательности [...]. Формат [=[...]=] может использоваться по умолчанию, если это необходимо.
# 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("недоступно!") end
';
}
# оценивается как "1234"
Другой подход к экранированию последовательностей PCRE - убедиться, что код Lua размещается в внешних файлах сценариев и выполняется с помощью различных директив *_by_lua_file. С этим подходом обратные косые черты обрезаются только парсером языка Lua и, следовательно, нужно экранировать только один раз.
-- test.lua
local regex = "\\d+"
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("недоступно!") end
-- оценивается как "1234"
Внешние файлы сценариев, представленные как строковые литералы Lua с длинными скобками, не требуют модификации.
-- test.lua
local regex = [[\d+]]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("недоступно!") end
-- оценивается как "1234"
Как уже упоминалось, последовательности PCRE, размещенные в директивах *_by_lua_block {} (доступных после выхода версии v0.9.17), не требуют модификации.
# 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("недоступно!") end
}
}
# оценивается как "1234"
ПРИМЕЧАНИЕ Рекомендуется использовать by_lua_file, когда код Lua очень длинный.
Поддержка SSI не поддерживается
Смешивание SSI с ngx_lua в одном запросе Nginx полностью не поддерживается. Просто используйте только ngx_lua. Все, что вы можете сделать с SSI, можно сделать с помощью ngx_lua, и это может быть более эффективно при использовании ngx_lua.
Режим SPDY полностью не поддерживается
Некоторые API Lua, предоставленные ngx_lua, в настоящее время не работают в режиме SPDY Nginx: ngx.location.capture, ngx.location.capture_multi, и ngx.req.socket.
Отсутствующие данные в перенаправленных запросах
Nginx может завершить запрос преждевременно с (по крайней мере):
- 400 (Неверный запрос)
- 405 (Не разрешено)
- 408 (Таймаут запроса)
- 413 (Слишком большой объект запроса)
- 414 (Слишком длинный URI запроса)
- 494 (Слишком большие заголовки запроса)
- 499 (Клиент закрыл запрос)
- 500 (Внутренняя ошибка сервера)
- 501 (Не реализовано)
Это означает, что фазы, которые обычно выполняются, пропускаются, такие как фаза переписывания или доступа. Это также означает, что последующие фазы выполнения (например, log_by_lua) будут не иметь доступа к информации, что обычно задается в этих фазах.
Изменения
Изменения, внесенные в каждый релиз этого модуля, перечислены в журналах изменений пакета OpenResty:
https://openresty.org/#Changes
Тестовый пакет
Для запуска пакета тестов требуются следующие зависимости:
-
Версия Nginx >= 1.4.2
-
Модули Perl:
- Test::Nginx: https://github.com/openresty/test-nginx
-
Модули Nginx:
- ngx_devel_kit
- ngx_set_misc
- ngx_auth_request (это не требуется, если вы используете Nginx 1.5.4 и выше).
- ngx_echo
- ngx_memc
- ngx_srcache
- ngx_lua (т.е. этот модуль)
- ngx_lua_upstream
- ngx_headers_more
- ngx_drizzle
- ngx_rds_json
- ngx_coolkit
- ngx_redis2
Порядок, в котором эти модули добавляются во время конфигурации, важен, поскольку позиция любого фильтра в цепочке фильтрации определяет конечный вывод. Правильный порядок добавления указан выше.
-
Библиотеки Lua сторонних разработчиков:
-
Приложения:
- mysql: создайте базу данных 'ngx_test', предоставьте все привилегии пользователю 'ngx_test', пароль: 'ngx_test'
- memcached: слушает на порту по умолчанию, 11211.
- redis: слушает на порту по умолчанию, 6379.
Смотрите также скрипт сборки разработчика для получения дополнительной информации о настройке тестовой среды.
Чтобы запустить весь пакет тестов в режиме по умолчанию:
cd /path/to/lua-nginx-module
export PATH=/path/to/your/nginx/sbin:$PATH
prove -I/path/to/test-nginx/lib -r t
Чтобы запустить конкретные тестовые файлы:
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
Чтобы выполнить конкретный блок тестов в определенном тестовом файле, добавьте строку --- ONLY в блок тестов, который вы хотите выполнить, а затем используйте утилиту prove для запуска этого .t файла.
Существуют также различные режимы тестирования на основе mockeagain, valgrind и т. д. Смотрите документацию Test::Nginx для получения более подробной информации о различных продвинутых режимах тестирования. Также смотрите тестовые отчеты для тестового кластера Nginx, работающего на Amazon EC2: https://qa.openresty.org.
Также см.
Записи в блогах:
- Введение в графики пламени CPU в Lua-Land
- Как OpenResty и Nginx выделяют и управляют памятью
- Как зона общей памяти OpenResty и Nginx потребляет RAM
- Фрагментация памяти в OpenResty и общих память в зонах Nginx
Другие связанные модули и библиотеки:
- ngx_stream_lua_module для официального порта этого модуля для подсистемы Nginx "stream" (выполнение общих downstream TCP-коммуникаций).
- lua-resty-memcached библиотека, основанная на cosocket ngx_lua.
- lua-resty-redis библиотека, основанная на cosocket ngx_lua.
- lua-resty-mysql библиотека, основанная на cosocket ngx_lua.
- lua-resty-upload библиотека, основанная на cosocket ngx_lua.
- lua-resty-dns библиотека, основанная на cosocket ngx_lua.
- lua-resty-websocket библиотека как для сервера, так и для клиента WebSocket, основанная на cosocket ngx_lua.
- lua-resty-string библиотека на основе LuaJIT FFI.
- lua-resty-lock библиотека для неблокирующего простого API блокировки.
- lua-resty-cookie библиотека для манипуляции HTTP-куками.
- Маршрутизация запросов к различным запросам MySQL в зависимости от аргументов URI
- Динамическая маршрутизация на основе Redis и Lua
- Использование LuaRocks с ngx_lua
- Введение в ngx_lua
- ngx_devel_kit
- echo-nginx-module
- drizzle-nginx-module
- postgres-nginx-module
- memc-nginx-module
- Пакет OpenResty
- Инструмент Nginx Systemtap
Директивы
- 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
- 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_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
- 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
Основными строительными блоками скриптов Nginx с Lua являются директивы. Директивы используются, чтобы указать, когда будет выполняться код Lua и как будет использоваться результат. Ниже представлена диаграмма, показывающая порядок выполнения директив.

lua_load_resty_core
syntax: lua_load_resty_core on|off
default: lua_load_resty_core on
context: http
Эта директива устарела с выпуском v0.10.16 этого
модуля. Модуль resty.core из
lua-resty-core теперь обязан
загружаться при инициализации виртуальной машины Lua. Указание этой директивы не будет иметь
эффекта.
Эта директива была впервые введена в выпуске v0.10.15 и
ранее использовалась для необязательной загрузки модуля resty.core.
lua_capture_error_log
syntax: lua_capture_error_log size
default: none
context: http
Включает буфер указанного size для захвата всех сообщений журналов ошибок Nginx (не только тех, которые
созданы этим модулем или подсистемой http Nginx, но и всех остальных), не обращаясь к
файлам или дискам.
Вы можете использовать единицы, такие как k и m в значении size, как в
lua_capture_error_log 100k;
Как правило, буфер размером 4 КБ может обычно содержать около 20 типичных сообщений журналов ошибок. Так что считайте!
Этот буфер никогда не растет. Если он заполняется, новые сообщения журнала ошибок заменят самые старые в буфере.
Размер буфера должен быть больше максимальной длины одного сообщения об ошибке (которая составляет 4K в OpenResty и 2K в стандартном NGINX).
Вы можете прочитать сообщения в буфере в Lua с помощью функции get_logs() модуля ngx.errlog библиотеки lua-resty-core. Эта функция API Lua вернет захваченные сообщения журнала ошибок и также удалит эти уже прочитанные из глобального буфера захвата, освобождая место для любых новых данных журнала ошибок. По этой причине пользователю не следует настраивать этот буфер слишком большим, если пользователь быстро считывает данные ошибок журнала в буфер.
Обратите внимание, что уровень журнала, указанный в стандартной директиве error_log имеет эффект на эту систему захвата. Она захватывает только сообщения журналов уровня, который не ниже указанного уровня журнала в директиве error_log. Пользователь все еще может выбрать установку еще более высокого уровня фильтрации журнала на лету с помощью функции API Lua errlog.set_filter_level. Таким образом, это более гибко, чем статическая директива error_log.
Стоит отметить, что нет возможности захватить отладочные журналы
без сборки OpenResty или Nginx с опцией ./configure
--with-debug. И включение журналов отладки
крайне не рекомендуется в производственных сборках из-за высокой нагрузки.
Эта директива была впервые введена в выпуске 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
Указывает, следует ли использовать MIME-тип, указанный директивой
default_type для
значения по умолчанию заголовка Content-Type ответа. Отключите эту директиву, если по умолчанию не требуется заголовок ответа Content-Type для обработчиков Lua запросов.
Эта директива включена по умолчанию.
Эта директива была впервые введена в выпуске v0.9.1.
lua_malloc_trim
syntax: lua_malloc_trim <request-count>
default: lua_malloc_trim 1000
context: http
Запрашивает у основного libc программного библиотеки освободить свою кэшированную свободную память обратно в операционную систему каждые
N запросов, обработанных ядром Nginx. По умолчанию N составляет 1000. Вы можете настроить счетчик запросов,
используя свои собственные числа. Более мелкие числа означают более частые освобождения, что может привести к более высокому потреблению времени ЦП
и меньшему объему памяти, в то время как более крупные числа обычно приводят к меньшему времени навигации ЦП и относительно большему объему памяти.
Просто настройте число на свой собственный случай использования.
Настройка параметра на 0 фактически полностью отключает периодическую обрезку памяти.
lua_malloc_trim 0; # отключить обрезку полностью
Текущая реализация использует обработчик фаз журналов Nginx для подсчета запросов. Поэтому наличие директив
log_subrequest on в nginx.conf
может ускорить подсчет, когда участвуют подзапросы. По умолчанию, только "основные запросы" считаются.
Обратите внимание, что эта директива не влияет на память, выделяемую собственным аллокатором LuaJIT на основе системного вызова mmap.
Эта директива была впервые введена в выпуске v0.10.7.
lua_code_cache
syntax: lua_code_cache on | off
default: lua_code_cache on
context: http, server, location, location if
Включает или отключает кэширование кода Lua для кода Lua в директивах *_by_lua_file (таких как set_by_lua_file и
content_by_lua_file) и модулей Lua.
Когда отключен, каждый запрос, обслуживаемый ngx_lua, будет выполняться в отдельном экземпляре виртуальной машины Lua, начиная с версии 0.9.3. Таким образом, Lua файлы, указанные в set_by_lua_file,
content_by_lua_file, access_by_lua_file,
и т.д., не будут кэшироваться, а все используемые модули Lua будут загружаться с нуля. При сохранении этого разработчики могут воспользоваться подходом "редактировать-и-обновить".
Тем не менее, обратите внимание, что код Lua, написанный встраиваемым в файле nginx.conf,
таком как те, которые указаны в set_by_lua, content_by_lua,
access_by_lua, и rewrite_by_lua, не будет обновлен при редактировании встроенного кода Lua в вашем файле nginx.conf, потому что только парсер конфигурационного файла Nginx может правильно проанализировать файл nginx.conf, и единственный способ — перезагрузить конфигурационный файл,
отправив сигнал HUP или просто перезапустив Nginx.
Даже когда кэш кода включен, файлы Lua, загружаемые через dofile или loadfile
в *_by_lua_file не могут быть кэшированы (если вы сами не кэшируете результаты).
Обычно можно использовать директивы init_by_lua
или init_by_lua_file чтобы загрузить все такие файлы или просто сделать эти Lua-файлы настоящими модулями Lua
и загрузить их через require.
Модуль ngx_lua не поддерживает режим stat, доступный с модулем Apache mod_lua (пока).
Отключение кэша кода Lua крайне не рекомендуется для использования в производстве и должно использоваться только во время разработки, так как это имеет значительное негативное влияние на общую производительность. Например, производительность "hello world" примера Lua может упасть в десять раз после отключения кэша кода Lua.
lua_thread_cache_max_entries
syntax: lua_thread_cache_max_entries <num>
default: lua_thread_cache_max_entries 1024
context: http
Устанавливает максимальное количество записей, разрешенных в кэше объектов lua thread на уровне процессов рабочего.
Этот кэш повторно использует объекты lua thread GC среди всех наших "легких потоков".
Значение <num> равное нулю отключает кэш.
Обратите внимание, что эта функция требует LuaJIT OpenResty с новым C API lua_resetthread.
Эта функция была впервые введена в версии v0.10.9.
lua_regex_cache_max_entries
syntax: lua_regex_cache_max_entries <num>
default: lua_regex_cache_max_entries 1024
context: http
Указывает максимальное количество записей, разрешенных в кэше скомпилированных регексов на уровне процессов рабочего.
Регулярные выражения, используемые в ngx.re.match, ngx.re.gmatch, ngx.re.sub и ngx.re.gsub, будут кешироваться внутри этого кэша, если задана опция регулярного выражения o (то есть флаг компиляции единожды).
Допускается по умолчанию 1024 записи, и когда этот лимит достигается, новые регулярные выражения не будут кешироваться (как если бы опция o не была указана), при этом будет выдано одно, и только одно, предупреждение в файл error.log:
2011/08/27 23:18:26 [warn] 31997#0: *1 lua превышает максимальное количество записей кэша регексов (1024), ...
Если вы используете реализацию ngx.re.* модулей lua-resty-core загружая модуль resty.core.regex (или просто модуль resty.core), то для кэша используется LRU.
Не активируйте опцию o для регулярных выражений (и / или аргументы строк замены для ngx.re.sub и ngx.re.gsub, которые генерируются на лету и приводят к бесконечным вариантам, чтобы избежать достижения указанного лимита.
lua_regex_match_limit
syntax: lua_regex_match_limit <num>
default: lua_regex_match_limit 0
context: http
Указывает "лимит совпадений", используемый библиотекой PCRE при выполнении метода ngx.re API. Процитируем мануал по PCRE: "лимит ... влияет на количество возможности возврата, которое может иметь место".
Когда лимит достигнут, строка ошибки "pcre_exec() завершилась неудачно: -8" будет возвращена функциями ngx.re API.
Когда лимит установлен на 0, используется значение по умолчанию "лимита совпадений" при компиляции библиотеки PCRE. И это значение по умолчанию для этой директивы.
Данная директива была впервые введена в выпуске v0.8.5.
lua_package_path
syntax: lua_package_path <lua-style-path-str>
default: Содержимое переменной окружения LUA_PATH или встроенные по умолчанию значения Lua.
context: http
Устанавливает путь поиска модулей Lua, используемый скриптами, указанными в set_by_lua,
content_by_lua и других. Строка пути находится в стандартной форме пути Lua, и ;;
может использоваться для обозначения исходных путей поиска.
С версии v0.5.0rc29 в строке пути поиска может использоваться специальная нотация $prefix или ${prefix}, чтобы указать путь к server prefix, который обычно определяется с помощью параметра командной строки -p PATH при запуске сервера Nginx.
lua_package_cpath
syntax: lua_package_cpath <lua-style-cpath-str>
default: Содержимое переменной окружения LUA_CPATH или встроенные по умолчанию значения Lua.
context: http
Устанавливает путь поиска C-модулей Lua, используемый скриптами, указанными в set_by_lua,
content_by_lua и других. Строка cpath находится в стандартной форме cpath Lua, и ;;
может использоваться для обозначения исходного cpath.
С версии v0.5.0rc29 в строке пути поиска может использоваться специальная нотация $prefix или ${prefix}, чтобы указать путь к server prefix, который обычно определяется с помощью параметра командной строки -p PATH при запуске сервера Nginx.
init_by_lua
syntax: init_by_lua <lua-script-str>
context: http
phase: loading-config
ПРИМЕЧАНИЕ Использование этой директивы не рекомендуется после выхода v0.9.17. Вместо этого используйте директиву init_by_lua_block.
Похожая на директиву init_by_lua_block, но принимает Lua-код непосредственно в строковом литерале Nginx (который требует специального экранирования символов).
Например,
init_by_lua '
print("Мне не нужно дополнительное экранирование, например: \r\nblah")
'
Эта директива была впервые введена в выпуске v0.5.5.
init_by_lua_block
syntax: init_by_lua_block { lua-script }
context: http
phase: loading-config
Когда Nginx получает сигнал HUP и начинает перезагрузку конфигурационного файла, виртуальная машина Lua будет также вновь создана, и init_by_lua_block снова выполнится на новой виртуальной машине Lua. В случае, если директива lua_code_cache отключена (по умолчанию включена), обработчик init_by_lua_block будет запускаться с каждым запросом, поскольку в этом специальном режиме всегда создается отдельная виртуальная машина Lua для каждого запроса.
Обычно вы можете предварительно загрузить модули Lua при старте сервера с помощью этого хуока и воспользоваться оптимизацией копирования по запросу (COW), предоставляемой современными операционными системами. Вот пример предварительной загрузки модулей Lua:
# это выполняется прежде чем создать процессы рабочих nginx:
init_by_lua_block { require "cjson" }
server {
location = /api {
content_by_lua_block {
-- следующий require() просто вернет
-- уже загруженный модуль из package.loaded:
ngx.say(require "cjson".encode{dog = 5, cat = 6})
}
}
}
Вы также можете инициализировать хранилище shm lua_shared_dict на этой фазе. Вот пример для этого:
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"))
}
}
}
Но обратите внимание, что хранилище shm lua_shared_dict не будет очищено при перезагрузке конфигурации (например, через сигнал HUP). Поэтому, если вы не хотите переинициализировать хранилище shm в вашем коде init_by_lua_block, то просто установите пользовательский флаг в хранилище shm и всегда проверяйте флаг в вашем коде init_by_lua_block.
Поскольку код Lua в этом контексте выполняется до того, как Nginx создаст свои рабочие процессы (если таковые имеются), данные или код, загруженные здесь, будут пользоваться функцией Copy-on-write (COW) среди всех рабочих процессов, сохраняя много памяти.
Не инициализируйте свои собственные глобальные переменные Lua в этом контексте, потому что использование глобальных переменных Lua имеет штрафы по производительности и может привести к загрязнению глобального пространства имен (смотрите раздел Область переменных Lua для получения дополнительной информации). Рекомендуемый способ - использовать правильные файлы Lua module (но не используйте стандартную функцию Lua module(), чтобы определить модули Lua




