Пользователь
0,0
рейтинг
25 апреля 2014 в 17:32

Разработка → N2O: Erlang Web-фреймворк на WebSockets из песочницы tutorial

N2O Erlang Framework

ВВЕДЕНИЕ


Данный пост подразумевает хорошее интро в 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/

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.
  1. При запросе страницы /index браузеру передаётся HTML разметка, описанная в функции index:body().
  2. На странице выполняется JavaScript, инициализирующий подключение через WebSocket.
  3. Затем по WebSocket передаётся полезная нагрузка в виде JavaScript кода, и инициализации элементов страницы данными через index:event(init). Здесь одна из ролей JavaScript — например, создать для кнопки на клиенте событие в виде JS-функции, которая отправит на сервер информацию о нажатии.
  4. После клацанья по кнопке, на сервер приезжает Bert-Encoded сообщение под грибами Base64 и выполняется функция index:event(Term) где Term — это терм, описанный в поле рекорда кнопки: #button.postback. Например, кнопка #button{postback=sasay} после нажатия принудит выполниться функцию index:event(sasay).
  5. Сервер, в свою очередь, также в любое время может отсылать данные браузеру. Если данные шлются из другого процесса Х, то должны быть соблюдены условия: в 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 был бы 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.
@mag2000
карма
4,0
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (24)

  • 0
    Очень интересно, а то я все yaws да yaws.
    А вот интересно, такие тривиальные задачи, как скажем upload files мультипартом и т.д. он схавает?
    А то все эти мега фреймворки ерланговские обычно какие-то недописанные. Вроде и круть немеряная, а свяжешься и сплошное изобретение велосипедов
    • +1
      Ну yaws это даже не вчерашний день. Только ковбой.
      А какие там фреймворки? Кроме нитрогена все мертвое давно было. Вот Максим n2o запилил.
    • 0
      ковбой схавает. вот пример. работает для бранча master либо для тэга 0.10.0+ который со слов essen скоро зарелизится. файлы также можно лить через ws если клиент позволяет.
      n2o не cms. и он достаточно активно развивается, как и cowboy
      спасибо за первый комментарий :)
  • 0
    Ну наконец-то! Спасибо за статью на русском!
    Как любитель erlang я очень хотел попробовать N2O, но sample не заработал. Пробовал на 9 FreeBSD.
    Запустить тогда удалось только Nitrogen. Сейчас попробовал запустить на Debian — тоже ошибка.
    Надеюсь, с вашей помощью можно будет разобраться. В какой системе гарантировано работает sample?
    • +1
      С kerl (https://github.com/spawngrid/kerl) на 17.0 релизе гарантированно работает.
      • 0
        Чет у меня только r16b03
        • 0
          c r16b03 почему-то много чего не работает. Лучше обновить до 17.
          Или через kerl поставить нужный релиз.
          • 0
            Что именно? Как-то не сталкивался.
            • 0
              ну у ЧикагоБосса вообще написано что с r16b03 он не работает. И на что-то еще я натыкался, но с ходу не вспомню.
              • 0
                Эм. Чикаго босс? Оно живо?!
                • 0
                  ну обновления в репе свежие относительно были :)
                • 0
                  конечно живо, других способов удобно записать данные в БД пока что не видать.
        • 0
          А так?

          $ kerl update releases
          • 0
            Ну я б сам, конечно, не догадался ). Не, нету пока.
          • 0
            Вру, он просто в начале списка.
    • 0
      linux и macosx как минимум. для версии n2o из бранча master рекомендуется R17. «тоже ошибка» — это норма, но лучшим вариантом будет показать лог и услышать решение в IRC канале
      • +1
        _5HT: n2o должно работать даже на виндовсе с MSYS
  • +1
    Уже почти год с помощью n2o пишу большущий проект. Доволен как слон. Авторам респект. Мой отзыв есть на официальной странице фреймворка.
  • 0
    по стилю сразу было заметно влияние Zert'а :)
  • +1
    Где можно изучить пример написания приложения на n2o с применением БД (например, MongoDB или PostgreSQL)?
    • 0
      Простите, а где тут связь? n2o — это веб-фреймворк, а работа с БД стоит совершенно отдельно. Для работы с PostgreSQL я использую epgsql и соответствующий ему epgsql_pool. Вот кусок моего rebar.config:
          {epgsql,        ".*", {git, "git://github.com/wg/epgsql.git", "HEAD"}}, % Database
          {epgsql_pool,       ".*", {git, "git://github.com/burinov/epgsql_pool.git", "HEAD"}},
      
    • 0
      Создание своей БД:



      Также:
      • Various Backends Support: Mnesia, Riak, KAI, Redis, MongoDB
      • Supports Secondary Indexes for KAI, Mnesia, Riak and MongoDB


      Дополнительно:
  • 0
    Есть вероятность что вы прикрутите server-side аналог Polymer для современной верстки и шаблонизации?
    • 0
      За деньги можно это сдлеать. Но обычно рекомендуем Full Stack Erlang.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.