Подтвердить что ты не робот

Что такое модель concurrency в Openresty?

Мне сложно свернуть голову вокруг модели openresty (или nginx's) concurrency. Я читал область переменных Lua, которая объясняет время жизни переменных, но ничего не говорит о параллельном доступе к ним.

Трудно объяснить словами, поэтому позвольте мне попытаться объяснить с помощью кода. Представьте, что у меня есть этот модуль Lua:

local counter = {count = 0}

function counter.incr(amount)
  counter.count = counter.count + (amount or 1)
end

return counter

И затем я использую его в openresty следующим образом:

server {
  location /incr {
    content_by_lua '
      local counter = require 'counter'
      counter.incr(1)
    '
  }
  location /decr {
    content_by_lua '
      local counter = require 'counter'
      counter.incr(-1)
    '
  }
  location /count {
    content_by_lua '
      local counter = require 'counter'
      ngx.write(counter.count)
    '
  }
}

Я хочу понять модель concurrency, чтобы ответить на эти вопросы:

  • Если я выполняю 10 одновременных вызовов /incr, а позже я вызываю /count, могу ли я быть уверен, что результат будет 10 (я предполагаю, что нет, но почему)?
  • Если я выполняю 10 одновременных вызовов /incr и в то же время я делаю еще 10 до /decr, могу ли я быть уверен, что /count вернет 0?
  • Как количество работников влияет на результаты?
  • Как влияет на результат фазу, в которой происходит код (т.е. init_by_lua вместо content_by_lua)?
4b9b3361

Ответ 1

nginx использует основанную на событиях архитектуру, а это означает, что он использует один поток 1 с циклом события, который обрабатывает сокеты, когда они становятся готовыми для чтения или записи. Это означает, что запросы не обрабатываются одновременно, но несколько запросов могут быть быстро обработаны один за другим, хотя могут возникнуть задержки в обработке отдельных запросов, если есть задержки сокета/ввода-вывода.

Если я делаю 10 одновременных вызовов в /incr, а позже я вызываю/подсчитываю, могу ли я быть уверен, что результат будет 10 (я предполагаю, что нет, но почему)?

Да. Пока /count вызывается после завершения всех запросов /incr, результат будет равен 10. Представьте, что 9 запросов были завершены, но 10-й запрос был по какой-то причине задержан отправителем, а если /count был обработанный до того, как 10-й запрос был обработан nginx, в результате вы должны получить 9.

Если я делаю 10 одновременных вызовов в /incr, и в то же время я делаю еще 10 до /decr, могу ли я быть уверен, что /count вернет 0?

Да, но порядок, в котором эти запросы будут обрабатываться, не гарантируется. Обратите внимание: в этом случае вам не нужно блокировать свое состояние или использовать глобальные семафоры или что-то в этом роде. Вы можете столкнуться с проблемами, если у вас есть некоторые вызовы ввода/вывода между состоянием чтения и записью его (поскольку в то же время можно обрабатывать другой запрос), но это не то, что делает ваш пример.

Как влияет количество рабочих на результаты?

Экземпляры Lua разделяются между запросами, обрабатываемыми одним и тем же рабочим процессом, поэтому несколько сотрудников не собираются давать вам тот же результат. Все ваши запросы /incr могут обращаться к одному рабочему, но ваш запрос /count может обратиться к другому сотруднику с другим экземпляром Lua, который (все еще) имеет count, установленный в 0. Если вам нужно обмениваться данными между экземплярами, вам, вероятно, нужно использовать что-то вроде lua_shared_dict. См. Также раздел обмена данными для других параметров.

Как влияет на результат фазу, в которой происходит код (т.е. init_by_lua вместо content_by_lua)?

init_by_lua выполняется только тогда, когда главный процесс загружает файл конфигурации.

1 Я упрощаю, так как он может вызывать несколько экземпляров для обработки многоядерных систем и некоторых других случаев, насколько я помню.

Ответ 2

Я запускаю именно этот кусок Lua с 30000 попаданий за 30 секунд. Я вижу ровно 30000 успешных записей в журнале доступа, но конечное значение счетчика на разных запусках варьировалось от 13000 до 16000. Протестировано на 2-х ядерных ВМ. Очевидно, это не потокобезопасный код.