OpenResty FAQ | Привилегии процессов, фазы выполнения и многое другое
API7.ai
November 11, 2022
Эта статья содержит шесть часто задаваемых вопросов:
1. Привилегии привилегированного процесса
В: Что такое привилегированный процесс? Как непривилегированный пользователь может получить права root? Можете ли вы привести несколько сценариев использования привилегированного процесса?
О: Привилегии привилегированного процесса такие же, как у процесса master. Если вы запускаете OpenResty от имени непривилегированного пользователя, то процесс master наследует привилегии этого пользователя, что означает, что "привилегированный процесс" сейчас не имеет прав.
Легко понять, что при запуске процесса обычным пользователем у него не будет прав root.
Что касается сценариев использования привилегированного процесса, мы обычно используем его для задач, требующих высоких привилегий, таких как очистка логов и перезапуск OpenResty. Однако будьте осторожны, не используйте привилегированный процесс для выполнения задач рабочего процесса из-за рисков безопасности.
Один разработчик запускает все задачи timer в привилегированном процессе. Почему он так делает? Потому что существует только один привилегированный процесс, и таким образом timer не запускается повторно.
Разработчик "умный", так как он достиг цели без использования worker.id. Однако не забывайте, что это очень опасно, если задача timer зависит от ввода клиента.
2. Фазы и отладка
В: После выполнения ngx.say('hello'), будет ли OpenResty сразу отвечать клиенту после выполнения остальной логики в текущей фазе? Это означает, что он не будет продолжать выполнение последующих фаз.
О: Нет. Мы можем посмотреть на его фазу выполнения:

Вы можете сначала протестировать ngx.say в фазе content, а затем использовать ngx.log в фазе log или body filter, чтобы вывести лог.
В предыдущих статьях я не упоминал конкретно проблему отладки кода в OpenResty, что может вызывать путаницу у разработчиков.
В OpenResty нет продвинутых функций для отладки кода, таких как точки останова (есть некоторые платные плагины, но я их не использовал), и вы можете использовать только ngx.say и ngx.log для просмотра вывода. Именно так все разработчики, которых я знаю, делают отладку, включая авторов и контрибьюторов OpenResty. Поэтому вам нужны надежные тестовые случаи и логи отладки в качестве гарантии.
3. Практика использования ngx.exit
В: В одной из предыдущих статей было описание: HTTP Status Code в OpenResty имеет специальную константу ngx.OK. После выполнения ngx.exit(ngx.OK) запрос выходит из текущей фазы и переходит к следующей фазе, вместо того чтобы сразу вернуться клиенту.
Я помню, что ngx.OK не следует считать HTTP статус-кодом, его значение равно 0. Мое понимание:
- После выполнения
ngx.exit(ngx.OK),ngx.exit(ngx.ERROR)илиngx.exit(ngx.DECLINED)запрос выходит из текущей фазы и переходит к следующей фазе. - Когда
ngx.exit(ngx.HTTP_*)принимает различные HTTP статус-кодыngx.HTTP_*в качестве параметра, он сразу отвечает клиенту.
Я не знаю, правильно ли мое понимание.
О: Относительно вашего первого вопроса, ngx.ok не является HTTP статус-кодом, а константой в OpenResty со значением 0.
Что касается второго вопроса, официальная документация для ngx.exit может быть точным ответом:
-
Когда status >= 200 (т.е. ngx.HTTP_OK и выше), он прерывает выполнение текущего запроса и возвращает статус-код в nginx.
-
Когда status == 0 (т.е. ngx.OK), он только выходит из текущего обработчика фазы (или обработчика контента, если используется директива content_by_lua*) и продолжает выполнение последующих фаз (если они есть) для текущего запроса.
Однако в документации не упоминается, как OpenResty обрабатывает ngx.exit(ngx.ERROR) и ngx.exit(ngx.DECLINED). Мы можем провести тест следующим образом:
location /lua { rewrite_by_lua "ngx.exit(ngx.ERROR)"; echo hello; }
При посещении этого location вы можете увидеть, что HTTP код ответа пуст, тело ответа также пусто, и он не переходит к следующей фазе выполнения.
По мере углубления в процесс изучения OpenResty вы обязательно обнаружите, что ни документация, ни тестовые случаи не могут ответить на ваши вопросы. В этот момент вам нужно создавать свои тестовые случаи для проверки своих идей. Вы можете делать это вручную или добавлять тесты в набор тестовых случаев, созданный test::nginx.
4. Переменные и состояние гонки
В: Как упоминалось ранее, область видимости переменной ngx.var находится между модулями nginx C и lua-nginx-module.
-
Я не совсем понимаю это. С точки зрения запроса, это означает один запрос в рабочем процессе?
-
Мое понимание: когда мы манипулируем переменными внутри модуля. Если между двумя операциями есть блокирующая операция, может существовать состояние гонки. Так что если между двумя операциями нет блокирующей операции, и так получилось, что текущий процесс попадает в очередь готовых, когда время CPU истекает, возможно ли существование состояния гонки?
О: Давайте рассмотрим эти вопросы.
Во-первых, относительно переменной ngx.var, ваше понимание верно. Жизненный цикл ngx.var совпадает с запросом, и он исчезает, когда запрос завершается. Но его преимущество в том, что данные могут передаваться между модулями C и кодом Lua, что невозможно другими способами.
Во-вторых, состояние гонки может возникнуть, если между двумя операциями есть операция yield, а не блокирующая операция. Состояние гонки не возникает, если есть блокирующая операция. Другими словами, пока вы не передаете инициативу событийному циклу NGINX, состояния гонки не будет.
5. shared dict не требует блокировки
В: Если несколько рабочих процессов сохраняют данные одновременно, нужно ли добавлять блокировки?
Например:
resty --shdict 'dogs 10m' -e 'local dogs = ngx.shared.dogs local lock= ngx.xxxx.lock lock.lock() dogs:set("Jim", 8) lock.unlock() local v = dogs:get("Jim") ngx.say(v) '
О: Здесь не нужно добавлять блокировку, потому что независимо от операции get или set, операции с shared dict являются атомарными. OpenResty уже предусмотрел подобную обработку блокировок.
6. Операции с временем в OpenResty
В: Использование ngx.now() для получения времени происходит в фазе восстановления функции resume?
О: NGINX разработан с приоритетом производительности и кэширует время. Мы можем проверить это по исходному коду ngx.now:
static int ngx_http_lua_ngx_now(lua_State *L) { ngx_time_t *tp; tp = ngx_timeofday(); lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L)); return 1; }
Как видите, за функцией ngx.now(), которая получает текущее время, стоит функция ngx_timeofday NGINX. Функция ngx_timeofday является макросом:
#define ngx_timeofday() (ngx_time_t *) ngx_cached_time
Здесь значение ngx_cached_time обновляется только в функции ngx_time_update.
Итак, вопрос сводится к "когда вызывается функция ngx_time_update?" Если вы проследите это в исходном коде NGINX, вы увидите, что вызовы ngx_time_update происходят в событийном цикле, тогда эта проблема решена.
Итог
Вы также можете понять через эти вопросы, что преимущество открытых проектов в том, что вы можете следовать за подсказками и искать ответы в исходном коде, что даст вам ощущение решения задачи.
Надеюсь, что через общение и вопросы-ответы я смогу помочь вам превратить то, что вы изучаете, в то, что вы получаете. Вы также можете поделиться этой статьей, и мы будем общаться и улучшаться вместе.