
ВВЕДЕНИЕ
Данный пост подразумевает хорошее интро в N2O на русском.
Что такое Erlang/OTP Web Framework N2O и в чём его фишка для веб-разработки, можно узнать на странице в github и официальном сайте SynRC. Там всё как вы любите с графиками и презентациями.
А здесь рассмотрим принципы работы фреймворка и поговорим о вечном.
Рассматриваемая версия N2O: 1.1.0
Всегда интереснее видеть результат, нежели говорить о нём, предлагаю сначала установить N2O себе на компьютер а уже потом вникать в его внутренности. Так нагляднее.
Получить ответы на возникшие вопросы и увидеть рекомендации можно на официальном канале IRC #n2o на FreeNode.net.
УСТАНОВКА
Устанавливаем Erlang если он ещё не установлен. Установили.
Скачиваем N2O, компилируем и запускаем:
git clone git://github.com/5HT/n2o.git
cd n2o/samples
make && make console
Смотрим:
127.0.0.1:8000/

Открыв несколько окон можно початиться самому с собой.
МНОГО ТЕОРИИ
НИША
Предназначен в первую очередь для разработки горизонтально масштабируемых решений с низкой задержкой на обработку клиентских запросов.
Онлайн-игры, чаты, инстаграммы, твиторы — это не совсем то, для чего создавался Erlang, но то, где нужно воспользоваться N2O.
ПОД КАПОТОМ
N2O это переработанный проект Nitrogen который немного загрустил. В N2O мы имеем быстрейший вэб сервер Cowboy, работу через WebSockets, обмен бинарными данными везде где только возможно, HTML и DTL шаблоны, минимальное количество JavaScript без использования сторонних библиотек.
Сравнение веб-фреймворков на Erlang можно увидеть по ссылке.
Возможность использования:
- NoSQL решений, таких как Mnesia, RIAK, KAI через простую абстрактную модель в виде приложения KVS.
- Библиотеки авторизации AVZ (Facebook, Google, Twitter, Github, Microsoft).
- Библиотеки MQ-очередей MQS для RabbitMQ.
- Интерпретатора Erlang кода в JavaScript с возможностью проверки во время компиляции — Shen.
- Любых других Erlang решений, просто добавив в приложение.
В N2O реализована компиляция исходников на лету, даже без порчи открытых сессий, на основе Sync. Разработка становится всё человечнее.
С Sync ты можешь кодить без дрочилова (перев.)
Обмен данными сервера с клиентами реализуется посредством WebSockets и имеет минимальный оверхед. Возможные форматы передаваемых данных:
- BLOB (RAW Binary), идеально для изображений
- Bert Encoded, для Erlang-термов
- Pickled, закодированные данные в Base64, либо AES/RIPEMD160
Кластеризация и отказоустойчивость для Веба стала доступна как никогда прежде, с максимальной простотой разработки.
_5HT: Чтобы ты не е___cя. Твое дело клац-клац и в продакшен
МОДЕЛЬ ПОВЕДЕНИЯ
N2O построен как и сам Erlang на передаче сообщений, но только не от процесса к процессу, а от клиента к серверу. Назову это событийной моделью.
Опишем таймлайн работы встроенного в N2O примера n2o_sample.
- При запросе страницы
/index
браузеру передаётся HTML разметка, описанная в функцииindex:body()
. - На странице выполняется JavaScript, инициализирующий подключение через WebSocket.
- Затем по WebSocket передаётся полезная нагрузка в виде JavaScript кода, и инициализации элементов страницы данными через
index:event(init)
. Здесь одна из ролей JavaScript — например, создать для кнопки на клиенте событие в виде JS-функции, которая отправит на сервер информацию о нажатии. - После клацанья по кнопке, на сервер приезжает Bert-Encoded сообщение под
грибамиBase64 и выполняется функцияindex:event(Term)
гдеTerm
— это терм, описанный в поле рекорда кнопки:#button.postback
. Например, кнопка#button{postback=sasay}
после нажатия принудит выполниться функциюindex:event(sasay).
- Сервер, в свою очередь, также в любое время может отсылать данные браузеру. Если данные шлются из другого процесса
Х
, то должны быть соблюдены условия: вindex:event(init)
главный процесс, обслуживающий клиента (вкладку браузера), должен быть зарегистрирован под неким именем вызовомwf:reg/1
, а в процессеХ
после окончания генерации кода для обновления страницы (wf:insert/2
,wf:wire/1
и т.п.) должна быть вызвана функцияwf:flush/1
для отправки кода обновления браузеру, где он затем будет выполнен JS-машиной.
Первое что приходит на ум — страница динамически изменяется не на основе вызовов функций из файла с JavaScript, а основываясь на полученном JavaScript от сервера в реальном времени.
И это нативный интерактивный режим для вэб-приложения без использования костылей вроде AJAX и LongPooling Comet. Будущее уже здесь, котята. Название ему — WebSockets.
Таблицу поддержки WS браузерами можно увидеть здесь, а проверить свой браузер здесь.
ПОДРОБНЕЕ О СТРУКТУРЕ ПРИЛОЖЕНИЙ
Если знакомство с Erlang произошло недавно или только что, скорее всего возникнут вопросы: что где лежит и в каком месте проявлять свой креатив.

Выделены те папки и файлы, которые могут быть подвержены редактированию. Рассмотрим ключевые части.
n2o_sample
N2O уже содержит в себе пример пользовательского приложения — n2o_sample. n2o_sample — это отдельное Erlang приложение, которое работает на N2O. Как видно на картинке выше, оно находится в директории
apps/
— это коллекция пользовательских приложений, можем довавлять туда свои, если потребуется разделить n2o_sample на два или более независимых приложения.Также n2o_sample, конечно же, можно переименовать или заменить чем-нибудь другим. Но для начала я бы не советовал этим заниматься, а пользоваться имеющимся кодом как отправной точкой в разработке своего приложения.
Application list
При запуске n2o_sample, он, как и полагается Erlang приложению, запускает все приложения от которых зависит (зависимости из
deps/
, и пользовательские, при их наличии, из apps/
). Этот код находится в n2o_sample/src/web_app.erl
:-module(web_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
application:start(crypto),
application:start(sasl),
application:start(ranch),
application:start(cowboy),
application:start(gproc),
application:start(mimetypes),
application:start(syntax_tools),
application:start(compiler),
application:start(erlydtl),
application:start(rest),
application:start(n2o),
web_sup:start_link().
stop(_State) -> ok.
Функция
web_sup:start_link()
запускает сам n2o_sample.Но если мы захотим расширить этот список зависимостей — надо знать, что это не единственное место с перечислением приложений. Также надо пофиксить ещё два файа:
reltool.config
и .applist
, последний создаётся после первой команды make в консоли. Держать в трёх местах одно и то же — временный костыль от @darkproger, но он обещал всё пофиксить (обещанного три года ждут).sys.config
Сюда сохраняются параметры для всех приложений. В один файл. Удобно. Потом из кода звоним в
application:get_env(App,Key)
и всё.vm.args
Здесь можно указать ключи для виртуальной машины а также переменные окружения (вместо
$ export SOME_PARAM=value
).priv/static/
Директория, переданная веб-серверу Cowboy как файлопомойка “статики”. Здесь можно размещать JavaScript код, закидывать пикчи и хентай.
n2o_sample/src/web_sup.erl
:dispatch_rules() ->
cowboy_router:compile(
[{'_', [
{"/static/[...]", cowboy_static,
{priv_dir, ?APP, <<"static">>,
[{mimetypes,cow_mimetypes,all}]}},
****
СТРАНИЦЫ
Для создания динамических страниц N2O даёт возможность включения HTML файлов в проект как DTL шаблоны. В директории
deps/erlydtl/
находится приложение ErlyDTL которое предназначено для компиляции DTL шаблонов в Erlang байт-код.Сами шаблоны располагаются в директории
n2o_sample/priv/templates/
и выглядят как HTML файлы с объявлениями внешних источников данных через слова, заключённых в двойные фигурные скобки {{ }}
.Таким образом мы можем “накидать” HTML макет со статической информацией, а динамическую вынести в Erlang код через подключение
{{ }}
.Для примера рассмотрим тело функции login:main/0, которая отдаёт браузеру первоначальное состояние страницы при переходе по адресу
http://127.0.0.1/login/
:#dtl{ file = "login",
app=n2o_sample,
bindings=[
{title,title()},
{body,body()}
]}.
- Здесь
login
— это имя шаблона: /priv/static/template/index.html; title
иbody
— именованные включения в HTML шаблоне {{title}} и {{body}};title()
иbody()
— функции, результат которых будет подставлен в HTML шаблон.
СОБЫТИЯ
Обмен данными в N2O на клиенте реализован через JavaScript, с помощью подгружаемых на страницы файлов
/deps/n2o_scripts/n2o/bullet.js
(инициализация WebSocket подключения) и n2o.js
(обработка полученных данных).Со стороны сервера эндпоинтами служат:
/n2o/src/endpoints/cowboy/bullet_handler.erl
(инициализация WebSocket подключения) и n2o_bullet.erl
(обмен данными).API
Разберём основные API-функции, с которыми возникает больше всего вопросов при знакомстве с N2O.
wf:comet/1, wf:async/1, wf:async/2
Регистрируют процесс под уникальным именем (
“comet”
для comet/1
и async/1
) в рамках ноды через global:register_name/2
. Если уже зарегистрирован, возвращают его Pid.wf:flush/1
Изымает через
wf_context:actions/0
сохранённые в стейте текущего процесса изменения для страницы и отсылает их через wf:send/2
процессу (в рамках ноды), имя которого передано в параметре.wf:reg/1, wf:reg/2 (?REGISTRATOR = n2o_mq)
Регистрируют процесс как Property под неуникальным именем в рамках ноды через GProc. Повторно зарегистрировать процесс через GProc под тем же именем не получится, состояние регистрации хранится в стейте процесса (
get/1
, put/1
) и будет возвращён терм skip
(n2o_mq.erl
). Дополнительная инфа на русском по Gproc тут.Регистратором по умолчанию является модуль
n2o_mq
но его можно переопределить, например для возможности регистрировать процессы в рамках кластера.wf:send/2 (?REGISTRATOR = n2o_mq)
Отсылает сообщение, переданное в функцию вторым аргументом, процессу, зарегистрированному через
wf:reg/1
или wf:reg/2
.wf:q/1
Извлекает данные, переданные от клиента через, так называемые, элементы обратной передачи, например:
body() ->
[ #textbox{ id=message },
#button{ postback={button_pressed}, source=[message] } ].
event({button_pressed}) ->
wf:info("Message: ~p",[wf:q(message)]);
В консоль будет напечатан текст, содержащийся на момент нажатия кнопки в текстбоксе.
wf:qs/1
Извлекает параметры из HTTP форм, например:
wf:qs(<<"x">>)
извлечет <<"ABC">>
если URL был бы localhost:8000/index?x=ABC
.wf:wire/1
Может принимать аргументом как JavaScript текст, так и рекоды событий, объявленные в секции “Actions” файла
/n2o/include/wf.hrl
, например: wf:wire(#alert{text="Привет!"})
. JavaScript будет также обёрнут в #wire{actions=JS}
, что приведет к конструкции wf:wire(#wire{sctions=JS})
.wf:update/2, wf:insert_top/2, wf:remove/1 и другие
Это частные случаи
wf:wire/1
, включающие готовый JavaScript код для изменения DOM в браузере клиента.wf:info, wf:warning, wf:error
Это функции, которыми рекомендуется пользоваться, вместо
error_logger:info_msg/1
и остальных соответственно.wf:f, wf:to_list, wf:to_binary, wf:html_encode, wf:url_encode, wf:hex_encode и другие
Распологаются в секции “Convert and Utils API” файла
/n2o/src/wf.erl
. Все они являются надстройками над стандартными функциями Erlang, но более умные и не такие деревянные. wf:f
— аналог io_lib:format
, далее по списку функции-конвертеры принимающие на вход любой терм, затем более специфичные для веба функции. Нет смысла их все подробно здесь описывать, была цель только показать, что они существуют.wf:pickle/1, wf:depickle/1 (?PICKLER = n2o_pickle)
Кодер и декодер термов для передачи системных вызовов между клиентом и сервером.
n2o_pickle
кодирует в Base64, в то время как n2o_secret использует AES/RIPEMD160 шифрование с произвольным ключом.На этом по API всё, дополнительную информацию можно почерпнуть из официальной доки по N2O API.
СТОИТ ОБРАТИТЬ ВНИМАНИЕ
Ниже перечислены свободные программные продукты SynRC, способные ускорить в десятки раз вывод в продакшн Вашего проекта на Erlang.
KVS
KVS — абстрактная модель K-V noSQL базы данных, умеющая в подмножества (feeds) таблицы через двусвязные списки и вторичные индексы (
kvs:index/3
). На текущий момент возможно использование с Mnesia, RIAK и KAI.AVZ
AVZ — система авторизации через Twitter, Google, Facebook, Github и Microsoft.
Shen
Shen — интерпретатор Erlang кода в JavaScript. Позволяет использовать компилятор Erlang для проверки JavaScript.
MQS
MQS — MQ библиотека для RabbitMQ.
Feeds
Feeds — обработчик пула команд поддержания согласованности данных на всех нодах кластера, также кэш сервер.
SkyLine
SkyLine — пример интернет-магазина на N2O.
COUNTACH
Countach — социальная система и расширенный магазин приложений. Production-ready. Использует KVS, AVZ и Feeds. Основан на VOXOZ.
VOXOZ
VOXOZ — Открытая Erlang облачная платформа (PaaS). Использует Docker, ErlangOnXen. Больше информации на blog.docker.io.
ЗАКЛЮЧЕНИЕ
Чтобы информация усваивалась равномерно, на этом пока всё. Отдельное спасибо @mtreskin за советы в выборе архитектуры и наводкой на nitrogen и n2o; а также @5HT за бесконечные консультации 24/7 в IRC.