Три часто используемые библиотеки Lua Resty в OpenResty

API7.ai

January 13, 2023

OpenResty (NGINX + Lua)

Изучение языков программирования и платформ часто сводится к пониманию стандартных и сторонних библиотек, а не самого синтаксиса. После изучения их API и техник оптимизации производительности, нам нужно изучить использование различных библиотек lua-resty, чтобы расширить возможности OpenResty для большего количества сценариев.

Где найти библиотеку lua-resty?

По сравнению с PHP, Python и JavaScript, текущие стандартные и сторонние библиотеки OpenResty все еще относительно скудны, и найти подходящие библиотеки lua-resty не так просто. Однако, есть два рекомендуемых источника, которые помогут вам найти их быстрее.

Первая рекомендация — это репозиторий awesome-resty, поддерживаемый Aapo. Этот репозиторий организует библиотеки, связанные с OpenResty, по категориям и является всеобъемлющим, включая модули NGINX на C, библиотеки lua-resty, веб-фреймворки, библиотеки маршрутизации, шаблоны, фреймворки для тестирования и т.д. Это ваш первый выбор для ресурсов OpenResty.

Если вы не нашли подходящую библиотеку в репозитории Aapo, вы также можете посмотреть на luarocks, topm или GitHub. Там могут быть библиотеки, которые недавно стали открытыми и пока не получили большого внимания.

В предыдущих статьях мы уже изучили несколько полезных библиотек, таких как lua-resty-mlcache, lua-resty-traffic, lua-resty-shell и другие. Сегодня, в последней статье раздела об оптимизации производительности OpenResty, мы познакомимся с тремя уникальными периферийными библиотеками, все они были созданы разработчиками из сообщества.

Улучшение производительности ngx.var

Сначала рассмотрим модуль на C: lua-var-nginx-module. Как я упоминал ранее, ngx.var — это операция, которая относительно затратна по производительности. Поэтому на практике нам нужно использовать ngx.ctx в качестве слоя кэша.

Есть ли способ полностью решить проблему производительности ngx.var?

Этот модуль на C проводит эксперименты в этой области, и результаты впечатляют — производительность улучшается в 5 раз по сравнению с ngx.var. Он использует подход FFI, поэтому вам нужно сначала скомпилировать OpenResty с следующей опцией компиляции.

./configure --prefix=/opt/openresty \ --add-module=/path/to/lua-var-nginx-module

Затем используйте luarocks для установки библиотеки lua следующим образом:

luarocks install lua-resty-ngxvar

Метод, который здесь вызывается, также очень прост, требуется всего одна строка функции fetch. Она работает так же, как оригинальный ngx.var.remote_addr, чтобы получить IP-адрес клиента.

content_by_lua_block { local var = require("resty.ngxvar") ngx.say(var.fetch("remote_addr")) }

После понимания этих базовых операций, вам может быть любопытно, как этот модуль достигает значительного улучшения производительности. Как мы всегда говорим, "перед исходным кодом нет секретов". Давайте разберемся, как получить переменную remote_addr.

ngx_int_t ngx_http_lua_var_ffi_remote_addr(ngx_http_request_t *r, ngx_str_t *remote_addr) { remote_addr->len = r->connection->addr_text.len; remote_addr->data = r->connection->addr_text.data; return NGX_OK; }

Прочитав этот код, вы увидите, что этот подход Lua FFI такой же, как и подход lua-resty-core. Он имеет очевидное преимущество использования FFI для получения переменных напрямую, минуя оригинальную логику поиска ngx.var. Его недостаток очевиден: добавление функций на C и вызовов FFI для каждой переменной, которую вы хотите получить, требует времени и усилий.

Некоторые могут спросить: "Почему я говорю, что это требует времени и усилий? Разве код на C выше не выглядит достаточно простым?" Давайте посмотрим на источник этих строк кода, который находится в src/http/ngx_http_variables.c в коде NGINX.

static ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { v->len = r->connection->addr_text.len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; v->data = r->connection->addr_text.data; return NGX_OK; }

После просмотра исходного кода, тайна раскрыта! lua-var-nginx-module — это портировщик кода переменных NGINX, с оберткой FFI на внешнем слое, и таким образом он достигает оптимизации производительности. Это хорошая идея и хорошее направление для оптимизации.

При изучении библиотеки или инструмента, мы не должны останавливаться на уровне операций, но также задавать вопрос, почему мы это делаем, и смотреть на исходный код. Конечно, я также настоятельно рекомендую вам вносить код для поддержки большего количества переменных NGINX.

JSON Schema

Здесь я представляю библиотеку lua-resty: lua-rapidjson. Это обертка вокруг rapidjson, открытой библиотеки JSON от Tencent, известной своей производительностью. Здесь мы сосредоточимся на отличии от cjson: поддержка JSON Schema.

JSON Schema — это общий стандарт, который позволяет нам точно описывать формат параметров в интерфейсе и то, как они должны быть проверены. Вот простой пример:

"stringArray": { "type": "array", "items": { "type": "string" }, "minItems": 1, "uniqueItems": true }

Этот JSON точно описывает, что параметр stringArray является массивом строк, и что массив не может быть пустым, а элементы массива не могут повторяться.

lua-rapidjson позволяет нам использовать JSON Schema в OpenResty, что может принести большую пользу для проверки интерфейсов. Например, для интерфейса ограничения количества, описанного ранее, мы можем использовать следующую схему для описания:

local schema = { type = "object", properties = { count = {type = "integer", minimum = 0}, time_window = {type = "integer", minimum = 0}, key = {type = "string", enum = {"remote_addr", "server_addr"}}, rejected_code = {type = "integer", minimum = 200, maximum = 600}, }, additionalProperties = false, required = {"count", "time_window", "key", "rejected_code"}, }

Вы обнаружите, что это может привести к двум очень очевидным преимуществам:

  1. Для фронтенда, фронтенд может напрямую повторно использовать это описание схемы для разработки интерфейса и проверки параметров, не заботясь о бэкенде.
  2. Для бэкенда, бэкенд напрямую использует функцию проверки схемы SchemaValidator из lua-rapidjson для определения легитимности интерфейса, и нет необходимости писать дополнительный код.

Общение между Worker

Наконец, я хочу рассказать о библиотеке lua-resty, которая позволяет общаться между воркерами в OpenResty, где нет механизма прямого общения между воркерами, что создает много проблем. Давайте представим сценарий:

У службы OpenResty есть 24 процесса воркеров, и когда администратор обновляет конфигурацию системы через REST HTTP API, только один Worker получает обновление от администратора и записывает результат в базу данных, обновляя shared dict и lru cache внутри своего воркера. Как же остальные 23 воркера могут быть уведомлены об этом обновлении?

Необходим механизм уведомления между несколькими Worker, чтобы выполнить вышеуказанную задачу. В случае, если OpenResty не поддерживает это, нам приходится спасать ситуацию с помощью данных shared dict между воркерами.

lua-resty-worker-events — это конкретная реализация этой идеи. Он поддерживает номер версии в shared dict, и когда публикуется новое сообщение, он увеличивает номер версии на один и помещает содержимое сообщения в словарь с номером версии в качестве key.

event_id, err = _dict:incr(KEY_LAST_ID, 1) success, err = _dict:add(KEY_DATA .. tostring(event_id), json)

Также в фоновом режиме создается цикл polling с интервалом по умолчанию в 1 секунду с использованием ngx.timer, чтобы постоянно проверять изменения в номере версии:

local event_id, err = get_event_id() if event_id == _last_event then return "done" end

Таким образом, как только обнаруживается новое уведомление о событии для обработки, содержимое сообщения извлекается из shared dict на основе номера версии:

while _last_event < event_id do count = count + 1 _last_event = _last_event + 1 data, err = _dict:get(KEY_DATA..tostring(_last_event)) end

В целом, хотя lua-resty-worker-events имеет задержку в одну секунду, он все же реализует механизм уведомления между воркерами.

Однако, в некоторых сценариях реального времени, таких как push-уведомления, отсутствие прямого общения между процессами Worker в OpenResty может вызвать у вас некоторые проблемы. Для этого нет лучшего решения, но если у вас есть хорошие идеи, пожалуйста, обсудите их на Github. Многие функции OpenResty развиваются благодаря сообществу, чтобы построить здоровый экологический цикл.

Итог

Три библиотеки, которые мы представили сегодня, уникальны и открывают больше возможностей для приложений OpenResty. Наконец, интерактивная тема: нашли ли вы какие-либо интересные библиотеки вокруг OpenResty? Или что вы думаете о библиотеках, упомянутых сегодня? Вы можете отправить эту статью пользователям OpenResty вокруг вас, чтобы обмениваться опытом и прогрессировать вместе.