[Из песочницы] N2O: Erlang Web-фреймворк на WebSockets

ВВЕДЕНИЕ Данный пост подразумевает хорошее интро в 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 Смотрим: http://127.0.0.1:8000/N2O: Erlang Web Framework in Safari

Открыв несколько окон можно початиться самому с собой.

МНОГО ТЕОРИИ НИША Предназначен в первую очередь для разработки горизонтально масштабируемых решений с низкой задержкой на обработку клиентских запросов.Онлайн-игры, чаты, инстаграммы, твиторы — это не совсем то, для чего создавался 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 произошло недавно или только что, скорее всего возникнут вопросы: что где лежит и в каком месте проявлять свой креатив.image

Выделены те папки и файлы, которые могут быть подвержены редактированию. Рассмотрим ключевые части.

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 был бы http://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.

© Habrahabr.ru