Как стать автором
Обновить

Комментарии 28

А где сравнение с Lua? ;)
Есть deb пакет nginx + lua для 12.04, либо если кто напишет эквивалентные решения (хотябы для Markdown) то можно выложить и проверить. Сам в Lua не силен.
Расскажите подробнее для чего это используется? То есть можно работать с запросами на более низком уровне и при этом на Ruby? Или можно прям Rails (Middleman, Sinatra) -проект запустить минуя Passenger (Puma, Unicorn etc)?
Да именно для работы с запросами на низком уровне.
К примеру, увам необходимо ограничить доступ к файлам на сервере. Варианта два, использовать модуль для Nginx: nginx.org/ru/docs/http/ngx_http_secure_link_module.html Но он не сильно гибкий, а можно реализовать это используя либо Ruby либо Lua + Redis|Memcache или другую БД. Тогда логика может быть сколь угодно сложной + поведение при отказе опять же контролируется.

Вот даже пример есть: github.com/matsumoto-r/ngx_mruby/wiki/Use-Case#file-server-like-dropbox-link

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

Область применения подобных решений это сложная логика обработки запросов, либо постобработка. Заменой полноценной версии они не являются.
Очень круто! Спасибо за разъяснение :-)
А не синхронно ли пойдут запросы на базу?
А не встанет ли nginx колом если база хоть на секунду задумается?

Я боялся делать логику в Nginx с внешними данными. Когда всё в памяти — хорошо, можно делать. А вот ходить куда-то…
Про lua модуль могу сказать лишь в контексте работы с Redis (активно использую на чтение/запись) и MySQL (только на чтение). Там «из коробки» дается пул соединений, а все сетевое взаимодействие происходит асинхронно для nginx, но блокирующе для lua. Сам пул при этом можно настраивать (размеры, таймауты).
Да, похоже в Lua озаботились сильной интеграцией с nginx. Я больше на mod_perl, там асинхронность никто не обещает, поэтому избегал.

А вообще обходной путь через fastcgi backend и X-Accel-Redirect не намного сложнее, а софт стандартный.
У нас lua используется в задачах, где дергать backend выходит накладно. Для более «редких» запросов, да, можно и что-то отдельно завести: в нашем случае php и python.
Как насчет блокирующих операций в ruby? Реализация mruby для nginx умеет встраиваться в event loop самого nginx?
От Ruby как такового там только синтаксис и некоторый набор Core классов. Все остальное реализуется в C модулях. Посмотрел как там реализовано подключение тому-же Redis, то судя по всему используется блокирующее API => в идеале нужно переписать отдельно на модуль с поддержкой nginx.
В Lua работа с Redis реализована на самом Lua, а так как там реализована обвязка над сокетами через nginx event loop, то там иная ситуация.

Все это узко специализированное решение. Когда нет необходимости в неблокирующих вызовах, имеет место быть. Хотя все нужно мерить, сравнивать, проверять. Может что не углядел в коде и оно совсем не так как показалось.
Чем мне нравится lua модуль, так тем, что там идет упор на неблокирующие операции (диск, сеть...). Возможность писать линейный код с блокирующими вызовами, которые реально не блокируют, очень приятно. Если же в этом нет необходимости и можно блокироваться — тогда лучше уж, имхо, выносить логику на backend, а не выполнять ее в самом nginx.

Погонял чуть модули в сравнении (портировал на lua в меру своих познаний ruby) через siege в 10-300 потоков на 30 секунд.

Сопоставимые HelloWorld'ы
ngx.print('Hello ngx_lua/'..ngx.config.ngx_lua_version..' world!')

Nginx::rputs "Hello #{Nginx::module_name}/#{Nginx::module_version} world!"

Выполняются одинаково на уровне погрешности.

Пример с факториалом из mruby (и да простит меня сервер, на котором я это тестил)
local function fib(n)
    return n < 2 and n or fib(n-2) + fib(n-1)
end

ngx.print(fib(39))

def fib n
  return n if n < 2
  fib(n-2) + fib(n-1)
end

puts fib(39)


-c 20 -t 30S
mruby — 7 hits — 0.24 trans/sec
lua — 51 hits — 1.72 trans/sec

-c 10 -t 60S
mruby — 10 hits — 0.17 trans/sec
lua — 163 hits — 2.76 trans/sec


Как с файлами работать в mruby я не в курсе (если вообще можно). Если кто даст пример — погоняю.
P.S. В один поток (wget) mruby и lua считают 39fib за 19 и 2.7 сек соотвественно. Почему так медленно у mruby?
Хм, странно, посмотрю сейчас в сравнении с полноценной версией и без привязки к nginx.
$ time ./mruby fi.rb
63245986

real    0m16.064s
user    0m15.769s
sys     0m0.028s

Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
$ time lua fi.lua
63245986

real    0m10.548s
user    0m10.325s
sys     0m0.020s

ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-linux]
$ time ruby fi.rb
63245986

real    0m12.904s
user    0m12.525s
sys     0m0.036s


$ time ./mruby -b fi.mrb
63245986

real    0m15.483s
user    0m15.185s
sys     0m0.016s


Последняя версяя ruby обгоняет уже lua, но все же, mruby не на столько шустр, как его позиционирует автор. Хотя это все синтетика и не понятно как на других вести себя будет тестах.
Я тестировал на luajit. Обычный lua не рекомендуется авторами lua_nginx, а также, даже если собрать nginx с ним, не будут работать некоторые модули.

А вот на моей локальной машине:
$ time ruby /tmp/fib.rb
63245986

real 0m9.331s
user 0m9.322s
sys 0m0.011s

$ time /tmp/nl/mruby/bin/mruby /tmp/fib.rb
63245986

real 0m11.551s
user 0m11.507s
sys 0m0.008s

$ time luajit /tmp/fib.lua
63245986

real 0m0.884s
user 0m0.884s
sys 0m0.001s
Какую версию руби используете тоже укажите пожалуйста, есть большая разница.
На локальной машине:
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
LuaJIT 2.0.2
nginx version: nginx/1.6.0

На сервере:
ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux]
LuaJIT 2.0.0-beta9
nginx version: nginx/1.6.0
mruby 1.0.0 (2014-01-10)
ruby fib.rb 15.03s user 0.00s system 99% cpu 15.040 total

ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-darwin13.1.0]
ruby fib.rb 12.08s user 0.01s system 99% cpu 12.087 total

ruby 2.0.0p353 (2013-11-22 revision 43784) [x86_64-darwin13.0.0]
ruby fib.rb 11.11s user 0.01s system 99% cpu 11.123 total

ruby 2.1.1p76 (2014-02-24 revision 45161) [x86_64-darwin12.0]
ruby fib.rb 10.76s user 0.01s system 99% cpu 10.773 total

Какой-то mruby совсем грустный, на 50% медленнее MRI
Так понимаю, больше hits => больше rps?

Сам по себе mruby не умеет с файлами работать, нужно подключать mruby_io, но в ngix_mruby он уже подключен.
Чтение файла целиком:
File.read(filename)
Открытие файла на чтение
f = File.open(filename, 'r')
f.read

А на счет неблокируемости Lua реализации согласен. Но тут есть конечно вариант, реализовать сокеты для mruby поверх nginx API, тогда можно будет реализовать простейшие протоколы для того же redis, memcached и тп на чистом Ruby. Думаю есть куда развиваться модулю, если будет интерес со стороны разработчиков, то и реализации подтянутся.
hits тут — это сколько всего запросов было обработано за все время тестирования (30 и 60 секунд соотвественно).

С файлами погоняю вечерком и напишу сюда что получится.

Я весьма активно пользуюсь lua_nginx в своих задачах. Отличнейшая штука. Если ruby версия пойдет таким же путем, то может, наверное, сыскать успех у любителей синтаксиса данного ЯП.
Потестил по простому на файлах.
К слову, File в mruby не идет сразу в сборке. Все gems нужно руками указывать в build_config.rb (в формате url на github), потом пересобирать mruby и nginx. У вас в посте написано, что они уже идут в сборке. Как вы собирали?

И результаты:

local f = io.open('/dev/zero', 'rb')
for i = 1, 1000 do
    ngx.print(f:read(4096))
end
f:close()

f = File.open('/dev/zero', 'rb')
for i in 1..1000
    Nginx::rputs f.read(4096)
end
f.close


Второй тест тоже самое, но читаемый за запрос кусок уменьшен до 1024. Итоги:
chunk 4096
lua   - 3065 hits - 105.54 trans/sec
mruby - 785 hits  - 26.52 trans/sec

chunk 1024
lua   - 7645 hits - 262.99 trans/sec
mruby - 3305 hits - 112.61 trans/sec

Вот как-то так выходит.
Резюмируя все выше указанное, возникает резонный вопрос: Как автор получил те цифры что на приложенном графике?
lua- изначально быстрее руби…
сперва сравните бенчмарки для консольного приложения.

если сделать на любой движок обвязку, хоть nginx, libevent_http или любую иную, то он от этого ни быстрее, ни медленнее не станет… только если специально кривыми руками не замедлить.
В консоли ситуация такая же. Вот планирую потестить на файлах, хоть какое-то сравнение встраивания в nginx.
О качестве кода красноречиво повествуют эти строки:

    ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module);
    if (ctx == NULL) {
      ngx_log_error(NGX_LOG_ERR
        , r->connection->log
        , 0
        , "get mruby context failed."
      );
    }

    if (ctx->rputs_chain == NULL) {
      chain = ngx_pcalloc(r->pool, sizeof(ngx_mrb_rputs_chain_list_t));
      chain->out = ngx_alloc_chain_link(r->pool);
      chain->last = &chain->out;
    }


Проверить *ctx на NULL, а далее попытаться по нему обратиться — это весело, это гарантированный сигфолт.

И там такое повсюду, только открыл — сразу глазом зацепился. Практически вся аллокация памяти без проверок на то, что память нам действительно выделили. И это пожалуй ещё самые малые проблемы модуля.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории