Паззл из стран Mercator. Сервер (Erlang+YAWS)

Aug 3, 2013 11:43 · 636 words · 3 minute read mercator erlang

Содержание:

YAWS vs Apache Это третья статья из цикла об игрушке Mercator - паззл из стран. На мой взгляд, самая интересная часть - Erlang. Почему он, а не, например, Node.js? Мне уже давно хочется познакомиться с миром функционального программирования, а Erlang - наиболее приспособленый для практического применения. Его основная фишка - легковесные потоки, которые выполняются параллельно, друг другу никогда не мешая, т.к. в этом языке нет понятия “переменная”. Потоки, правда, могут общаться друг с другом через события. Для начала рекомендую ознакомиться со статьёй на RSDN. Это самая лучшая вводная статья на русском языке, которую я смог найти. Кстати, по поводу документации и прочих материалов - их валом! Самые интересные на мой взгляд я описал в статье. Так что не бойтесь приступить к изучению ;)

Настройка

Писать веб-сервер на чистом erlang мне показалось сумасшедшей идеей. На тот момент я нашёл несколько HTTP-серверов: YAWS, Cowboy и N2O. Последние 2 пока слишком сложны для моей задачи, к тому же я хотел поскорее начать и увидеть хоть какой-то результат. Так что остановился на YAWS. В нём реализована загрузка файлов, SOAP, веб-сокеты и прочие прелести. Однако, мне бы хотелось обратить внимание на файл настроек. Во-первых, надо создать по шаблону секцию server. Там всё очевидно - IP-адрес, порт и корневой каталог; во-вторых надо прописать в ebin_dir где будут храниться компилированные erl файлы. Внимание! это может быть только один каталог, так что для разных проектов либо разные инстансы YAWS, либо всё сваливать в этот каталог. Надеюсь, с этим проблем не возникнет, так что перейдём к кодингу. Erlang перед выполнением компилирует свои исходники в байт-код (*.beam), так что давайте сначала напишем make-файл для компиляции:

ERLC=/usr/bin/erlc
ERLCFLAGS=-o
SRCDIR=./src
BEAMDIR=./ebin

all:
  @ mkdir -p $(BEAMDIR) 
  @ $(ERLC) $(ERLCFLAGS) $(BEAMDIR) $(SRCDIR)/*.erl 
clean:
  @ rm -rf $(BEAMDIR)

По нему видно, что исходники (*.erl) будут лежать в каталоге src, а скопилированные файлы в ebin. Кстати, не забудьте добавить этот каталог в ebin_dir! Теперь для теста напишем небольшой скриптик index.yaws для отдачи динамического контента. Вставьте в тег body следующий код:

getGreeting(Name) ->
  "Hello, " ++ Name.

out(Arg) ->
  case queryvar(Arg, "name") of
    {ok, Name} -> {html, getGreeting(Name)};
    undefined -> {html, "Укажите имя"}
  end.

Логика простая - если передали GET-параметр name, то вывести приветствие, иначе - “Укажите имя”. Как видно, вывод осуществляется через функцию out в тегах erl. Эти теги имеют свою область видимости - нельзя написать один общий для файла блок, а потом из других дёргать его функции - надо выносить в erl-файл.

Модули

Разберём создание модуля на примере работы с базой данных. Для начала нам потребуется файл с драйвером для того же Postgres - sql_driver.erl. Он предоставляет функцию pgsql:connect и pgsql:query для подключения и выполнения запроса соответственно. Экспортироваться же будет всего одна функция run_sql.

-module(sql_driver). % объявляем модуль
-export([run_sql/1]). % делаем доступной для вызова функцию run_sql

% определяем константы
-define(host, "localhost").
-define(port, 5432).
-define(db, "mercator").
-define(user, "mercator").
-define(password, "mercator").

run_sql(Query) ->
  with_connection(
    fun(C) -> {ok, Cols, Rows} = pgsql:squery(C, Query) end
  ).

with_connection(F) ->
  with_connection(F, []).

with_connection(F, Args) ->
  Args2 = [{port, ?port}, {database, ?db} | Args],
  {ok, C} = pgsql:connect(?host, ?user, ?password, Args2),
  try
    F(C)
  after
    pgsql:close(C)
  end.

Цепочка вызовов будет следующая:

  1. вызов run_sql с sql-командой
  2. вызов with_connection с функцией, которую надо выполнить (из run_sql)
  3. вызов with_connection с функцией из run_sql и пустыми параметрами
  4. получение в C соединения с БД
  5. вызов функции из run_sql с параметром C
  6. не забываем закрыть соединение с БД

Теперь, если положить скомпилированную версию в ebin_dir, то функция run_sql будет доступна везде в YAWS'е. По коду можно заметить, что используется сторонний модуль pgsql. Его предоставляет пакет epgsql. Для его установки необходимо скачать пакет, распаковать в /usr/lib/erlang/lib и компилировать его (make compile). На этом, пожалуй, закончу. Я постарался дать вводную информацию для работы с Erlang и YAWS и поделиться полученными знаниями. Напоминаю, что код можно найти на github.

UPD

GeoPuzzle Проект переписан на другие технологии, улучшен и запущен на сайте https://geopuzzle.org.