12 февраля 2009 в 14:20

Erlang в Рисоваське, часть 2 — ответы на вопросы

Данная статья является продолжением цикла статей про язык программирования Эрланг/Erlang и его использование в нашем проекте Рисоваська. В первую очередь я хочу ответить на вопросы заданные в комментариях к первой статье, а вопросов было задано много. Итак продолжим:

Установка Erlang под Windows


Установить Erlang достаточно просто. Сначала скачиваем дистрибутив с официального сайта. Нам нужна последняя на данный момент версия R12B-5, колонка Windows binary (incl. documentation). После инсталляции в меня «Пуск» — «Все программы» появится пункт Erlang OTP R12B и в нем подпункт «Erlang». Если его выбрать, то запуститься безымянная нода вместе с оболочкой (shell) в которой уже можно выполнять эрланговский код, в том числе из моих примеров. Я буду придерживаться названия «нода», так как официального названия в русском языке пока не устоялось, хотя слово «node» и переводиться как «узел».

Вот ссылки про установку Эрланга под Mac OS X и Линукс (на английском).

Чтобы завершить установку, нужно в переменные среды Windows прописать следующие переменные:
1. Путь к директории, куда был инсталлирован Эрланг:
ERL_TOP = C:\Program Files\erl5.6.5
этот же путь, но с добавлением \bin, т.е. C:\Program Files\erl5.6.5\bin нужно прописать и в переменную PATH, если его там еще нет

2. Переменная HOME, если она у вас еще не прописана, например:
HOME = C:\Documents and Settings\tolik

3. Рекомендую сразу прописать путь к сторонним библиотекам Эрланга:
ERL_LIBS=C:\Work\erl_libs
В будущем все сторонние библиотеки, не входящие в поставку Erlang/OTP, кладите в эту директорию. Это позволит легко поставить новую версию Erlang/OTP, удалив старую, не переставляя при этом сторонние библиотеки. Кстати, новые версии Erlang/OTP выходят с регулярностью два раза в год.

И последнее, в директории HOME создайте файл ".erlang.cookie". Это обычный текстовый файл в котором храниться текстовый cookie в одну строку, например: «AMMVFJKDTENSFHDJDGDERSW». Этот cookie будет использоваться при взаимодействии нод между собой. Дело в том, что две ноды могут взаимодействовать между собой, только если у них одинаковые cookie's. Если вас пока не интересует взаимодействие нескольких нод между собой, то пропустите этот шаг.

В той же директории HOME удобно создать файл ".erlang" в котором можно писать код на Эрланге, который будет автоматически выполняться при старте любой ноды на данном компьютере. Обычно в этот файл пишут код добавляющий путь к исполняемым файлам вашего приложения в список поиска:

code:add_pathz("Путь к папке ebin вашего приложения").

Теперь вы можете запускать ноду прямо из командной строки просто набрав: «erl». Можно запускать ноду и без оболочки или как демон. Если вы запустили ноду без оболочки, то подключиться к ней вы сможете запустив вторую ноду (можно и на другом компьютере) выполнив:

erl -name test@192.168.0.11 -remsh main@192.168.0.11

где main@192.168.0.11 — название ноды к которой мы подключаемся, а test@192.168.0.11 — название новой ноды. После подключения вы можете выполнять все команды, как будто вы находитесь на первой ноде.

Более подробно параметры запуска описаны в модуле erl.

Emacs, как среда разработки


До недавнего времени почти единственным удобным вариантом писать и отлаживать программы на Эрланге был Emacs (по ошибке принимаемый за обычный текстовый редактор) в связке с Distel. Достаточно хорошую инструкцию по настройке и работе с обоими пакетами на русском языке можно найти здесь. В своей работе мы по прежнему используем именно их, хотя они и выглядят как древние динозавры, по сравнению с современными средами разработки.

Новая среда разработки: erlIDE


На замену Emacs, разрабатывается новая среда разработки erlIDE. Текущая версия 0.4.3 уже достаточно стабильна и не падает, как более ранние версии. В ней присутствуют почти все обычные для современных IDE инструменты: отладчик, контекстная подсказка, простая навигация по коду, подсветка синтаксиса и т.д. Инструкция по установке написана достаточно подробно и вопросов обычно не вызывает. erlIDE работает как плагин к Eclipse, поэтому вам нужно сначала поставить на свой компьютер Java (лучше не ниже 6-й версии), а потом Eclipse. Для работы будет достаточно Eclipse IDE for Java Developers (85 MB). Под Windows я бы рекомендовал сразу начать с erlIDE, чтобы не привыкать к Emacs.

А теперь вернемся непосредственно к языку.

Создание первого приложения


Для начала создайте папку, где у вас будет находиться проект. Внутри нее нужно создать следующие подпапки:
  • ebin — сюда будет класться скомпилированный код (.beam)
  • src — исходники приложения (.erl)
  • include — заголовочные файлы (.hrl)
  • doc — документация
  • priv — все остальное, что нужно приложению в работе, но не вошло в другие папки

Это стандартная структура приложения, рекомендуемая принципами OTP. В корневой папке приложения создайте файл emakefile со следующим содержимым:

{"src/*", [debug_info, {i, "include"}, {outdir, "ebin"}]}.

и тогда в оболочке ноды, находясь в корневой папке приложения, вы сможете просто выполнять команду: make:all(), чтобы перекомпилировать все модули приложения.

В подпапке «ebin» нужно создать файл your_application.app примерно с таким содержимым:
{application, your_application.app,
[{description, "Test application"},
{vsn, "1"},
{modules, [you_application, you_application_sup]},
{registered, []},
{applications, [kernel, stdlib, sasl]},
{mod, {your_application,[]}}
]}.


И последнее, в папке «src» нужно создать главный файл проекта: you_application.erl с минимальным содержимым:
-module(you_application).
-behaviour(application).

-export([start/2, stop/1]).

start(_Type, _Args) ->
your_application_sup:start_link().

stop(_State) ->
ok.


Таким образом мы создали минимальное приложение на Эрланге, которое пока ничего не делает. Так же пока не реализован главный супервизор приложения (о нем я напишу позже). Более подробно о создании приложения описано в документации OTP

Горячая замена кода


Очень полезной особенностью Erlang/OTP является горячая замена кода без остановки системы. Как это работает на примере одного модуля? Например, вы изменили код в одном из модулей приложения и хотите его подменить на лету в работающей системе. В этой ситуации в Эрланге совсем не обязательно останавливать все приложение. Достаточно в оболочке ноды набрать: l(your_module). и после выполнения этой команды будет работать уже новый код. Если говорить более подробно, то виртуальная машина Эрланга хранит в памяти две копии каждого модуля: текущую и предыдущую. Зачем нужно хранить предыдущую версию? Чтобы в случае неправильной работы нового кода быстро откатиться на предыдущую версию так же не останавливая работу приложения.

Конечно можно заменять на лету не только отдельный модуль, но и все приложение целиком. Для этого:
  • создаем файл обновления приложения (Application Upgrade File), в котором прописываем правила замены старой версии на новую, а так же зависимости между модулями во время замены версии
  • создаем файл обновления релиза (Release Upgrade File)
  • инсталлируем новую версию приложения поверх старой

Чтобы сократить размер статьи, за дополнительной информацией я отправляю в документацию, где эти шаги описаны более подробно.

Но что делать, когда у вас работает целая распределенная система, состоящая из нескольких серверов и вам нужно на них всех одновременно заменить код без остановки всей системы. К сожалению готового удобного решения Erlang/OTP тут не предлагает. Тем не менее в состав OTP входит модуль erl_boot_server, который позволяет при старте ноды загружать код приложения, включая файлы конфигурации с другой ноды. Тогда перегрузка всей системы может быть реализована так: сначала происходит горячая замена кода на главном сервере, код с которого загрузили остальные ноды при своем старте, используя модуль erl_boot_server. А потом всем нодам рассылается сообщение на перегрузку ноды. При получении такого сообщения на ноде выполняется команда init:restart/0 перегружающая ноду без ее полной остановки. После перегрузки все ноды забирают уже обновленный код с главного сервера. Примерно такой подход описан в двух статьях Setting up Erlang on Amazon EC2 (вторая часть статьи) и Upgrading your Erlang cluster on Amazon EC2.

Behaviours


Еще одной интересной особенностью языка являются behaviours, являющийся аналогами интерфейсов в традиционных языках, а еще точнее абстрактных классов. Если в начале модуля, например, написано -behaviour(application). — это значит две вещи: часть логики уже реализована в OTP и второе, в модуле должен быть реализован определенный набор callback-функций, без которых модуль не будет компилироваться. В нашем случае мы обязаны реализовать функции start и stop. За более детальной информацией отправляю к документации по behaviours.

Дай процессу умереть


Как я уже отмечал в своей первой статье высокая надежность систем построенных на Эрланг основывается не на там, что весь код написан так, что он не падает, а так, что если происходит какая-либо ошибка в рабочем процессе, то всегда есть наблюдающий за ним процесс, который исправит ситуацию и восстановит работоспособность системы. Действительно, чаще всего рабочий процесс обычно не знает точно, что делать в случае ошибки, поэтому более правильно просто умереть и дать разобраться в ситуации наблюдающему процессу. Делается это так:

  • во-первых, наблюдающий процесс выполняет знаменитую в Эрланге команду:
    process_flag(trap_exit, true).
    Это значит, что данный процесс ловит сообщения о падении связанных с ним процессов. После выполнения данной команды, если падает рабочий процесс, то в наблюдающий процесс приходит сообщение вида: {'EXIT', From, Reason} Важно, что без выполнения этой команды, в случае ошибки в рабочем процессе, падает по цепочке и все связанные с ним процессы, пока на пути не встретиться процесс, который выполнил process_flag(trap_exit, true).
  • во-вторых, наблюдающий процесс создает связанный с ним рабочий процесс:
    spawn_link(?MODULE, worked_function, [Arguments]).
  • и, в третьих, наблюдающий процесс начинает слушать сообщения:
    receive
    {'EXIT', From, Reason} ->
    supervising_function() % выполнить саму себя (см. ниже)
    end

В сумме, минимальный код наблюдающего процесса будет выглядеть так:
supervising_function() ->
process_flag(trap_exit, true).
spawn_link(?MODULE, worked_function, [Arguments]).
receive
{'EXIT', From, Reason} ->
supervising_function()
end
end.


Строительные кирпичики Erlang/OTP


Но совсем не обязательно писать самому с нуля наблюдающие процессы, процессы доступа к общим ресурсам и т.д. За неполных двадцать лет развития языка в Erlang/OTP был разработан надежный и отлаженные набор базовых компонентов на которых обычно и строиться приложение написанное на Эрланг. Вот они:
  • gen_server — базовая реализация сервера, поддерживающая клиент-серверное взаимодействие в системе. Замечу, что здесь понятие «сервер», не совпадает с общепринятым понятием, а означает специфический сервер Erlang/OTP. Обычно в системе он отвечает за доступ к общим ресурсам системы.
  • gen_fsm — базовый модуль, реализующий конечную машину состояний
  • gen_event — базовый модуль, реализующий функциональность обработки событий
  • и, конечно, supervisor — модуль предназначен для запуска, остановки и наблюдения за своими дочерними процессами. Чтобы не писать много теории приведу простой пример функции инициализации супервизора:
    init() ->
    RestartStrategy = one_for_one
    MaxRestarts = 10,
    MaxTimeBetRestarts = 3600,
    SupFlags = {RestartStrategy, MaxRestarts, MaxTimeBetRestarts},
    ChildSpecs = [{Some_Module, {Some_Module, Function, []}, permanent, TimeOut,
    worker, % тип дочернего процесса
    [Some_Module]}],
    {ok, {SupFlags, ChildSpecs}}.

    Данный код запустит один дочерний процесс, максимального кол-во перезапусков дочернего процесса = 10 с частотой не более 3600 мс.


Замечу, что во всех базовых компонентах, описанных выше (кроме супервизора), есть функция code_change, которая автоматически выполниться при горячей перезагрузке приложения. В этой функции вы можете написать код, обеспечивающий плавный переход от одной версии приложения к другой.

К сожалению, подробное описание каждого модуля и связей между ними займет несколько статей, к тому же они хорошо описаны в Design Principles. Да и за примерами использования ходить далеко не надо, берите любое, достаточно известное приложение или библиотеку, написанную на Эрланге и смотрите. Почти все они написаны с использованием этих базовых компонентов.

В завершении статьи хочу дать ссылку на хорошую статью в Википедии про Эрланг и две хорошие (1, 2) подборки ресурсов по Эрлагу, которую собрал Дима Смолин. Для тех, кому интересен язык Эрланг, этих ссылок вполне достаточно, чтобы глубоко погрузиться в язык.

Продолжение следует...


Как и в первой статье список тем для рассмотрения остается тот же: использование распределенной базы Mnesia (которая входит в состав Erlang/OTP), использование Amazon S3 и Amazon EC2 с примерами использования этих технологий на серверах Рисоваськи.
Анатолий Востряков @vostryakov
карма
98,0
рейтинг 0,0
Похожие публикации
Самое читаемое Разработка

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

  • +1
    полезную работу делаете, спасибо
    пожалуйста, поделитесь инвайтом со страждущими в русской ерланг рассылке
    • +1
      Спасибо и тебе! Извини, инвайт, считай, отдал своему хорошему другу на написание статьи.
      P.S. Комментарий ниже создал по ошибке.
      • 0
        ну значит следующим ,)
        • +1
          Следующим инвайтом обязуюсь поделиться в русской эрланг-рассылке, тому кто захочет писать статью про Эрланг :)
  • +1
    Спасибо и тебе! Извини, инвайт, считай, отдал своему хорошему другу на написание статьи.
  • 0
    Вот на емакс наезжать не надо, лучшей IDE до сих пор не придумали.
    • 0
      Да какие уж наезды. Сам в нем программирую :) Но я бы все же выбрал erlIDE, если бы сразу с него начал. Просто тогда он был еще не готов. Вот еще доработают erlIDE и наверно на него все же перейду. Лично мне все же «традиционные» графические IDE больше нравятся.
      • +1
        > Да какие уж наезды.

        Извини, показалось.

        > Но я бы все же выбрал erlIDE, если бы сразу с него начал. Просто тогда он был еще не готов. Вот еще доработают erlIDE и наверно на него все же перейду. Лично мне все же «традиционные» графические IDE больше нравятся.

        Фишка емакса — многоязыковые проекты и легкость автоматизации мелких задач. Если используется один язык — то лучше конечно писать в заточенной под него ИДЕ.
  • 0
    >gen_fsm — базовый модуль, реализующий конечную машину состояний
    Это то что обычно конечными автоматами называется? :-)
    • 0
      Да.
  • 0
    Спасибо, очень доходчиво и понятно пишете.
    Соответственно вопросы:
    Если ноды имеют имя, привязаное к определенному хосту, значит ли это, что приложение сильно зависимо от используемого сетевого окружения и оборудования? Есть ли возможность как-то гибко манипулировать списком имеющихся серверов? Как это настраивается?

    Как обстоят дела с reusability кода?

    Какие основные требования повлияли на выбор именно Erlang, когда вы выбирали язык для реализации Рисоваськи? Какие альтернативы рассматривались?

    Покорнейше благодарю.
    • 0
      Спасибо!

      Отвечаю на вопросы:
      Вообще-то ты можешь назвать ноду как хочешь. Я имею ввиду часть, которая пишется после знака "@", например: «super@supersite». Пока нет потребности взаимодействовать с другими нодами в сети проблем это вызывать не будет. Но если есть задача взаимодействовать с нодами на других, то тогда виртуальная машина полагается на нижестоящую операционную систему при определения IP адреса другой ноды при поиске.
      Списка имеющихся серверов в каждой конкретной виртуальной машине эрланга не храниться. Связь между двумя нодами устанавливается только при первой необходимости, например выполнить в Эрланге простой пинг другой ноды: net_adm:ping(name_of_other_node). То есть если просто стартовать одну ноду, то она не начнет искать другие ноды в сети сразу. По моему все это напоминает простую работу в сети. При обращении из браузера по адресу habrahabra.ru определяется IP адрес сервера через DNS. Эрланг, через операционную систему, поступает точно так же.
      Использовать повторно код можно, тут я не вижу отличий от других языков. Можно оформлять код в библиотеки и использовать их в других проектах. На практике у нас пока один проект на Эрланге, поэтому у нас пока нет такого опыта. Но если в следующем проекте, например, потребуется парсинг XML с проверкой по модели, то мы можем вполне взять кусок из текущего проекта.
      Требования были масштабируемость системы, простота написания системы, высокая надежность и выдерживание большой нагрузки на сервера. В общем мы рассматриваем наш проект сразу, как проект мирового уровня известности с миллионами пользователей. При выборе естественно выбирали и из традиционных языков C++, C#, Java, а так же Python, Ruby, PHP. На «традиционных» языках писать и отлаживать систему такого уровня обычно просто очень долго, а нам нужно было уложиться при разработке с нуля в 4-6 месяцев вместе с отладкой. Плюс желание высокой надежности системы сузили выбор до Эрланга.
  • 0
    гм, а к примеру определить список нод на которых выполняется приложение в одном месте можно каким-либо образом?
    чтобы наращивать мощность системы просто добавлением ноды?
    • 0
      Ответ на второй вопрос: да :) Но у нас сделано скорее наоборот. При запуске новая нода сразу запускает приложение, которое само включает ноду в уже работающую систему, тем самым наращивая мощность системы. Подход: «определить список нод» в одном месте ошибочен в том, что он централизованный. При выходе из строя этого центра выходит из строя и вся система. К тому же в EC2 при зависании компьютера ты теряешь на нем все данные, если они конечно не хранились на EBS.
      • 0
        ага, вот так. спасибо
      • 0
        Но список то нод заранее известен же? Так? Ведь для их работы нужно на них установить куку, значит список можно условно считать фиксированным.

        Просто я как то слабо представляют себе приложение, которое встраивается в распределенную систему не зная заранее возможный перечень доступных хостов.
        • 0
          Всей системе в целом список работающих нод можно считать известным, но это не обязательно на самом деле. Может, например, нода упала и вышла экстренно из системы.
          Сам Эрланг не требует для своей работы, чтобы каждая нода знала все работающие ноды сразу и постоянно, соответсвенно, можно и систему строить так, чтобы новая нода знала, только то, что ей нужно для своей работы.
          Если говорить про нашу систему конкретно, то у нас стартующая нода может выполнять одну из перечисленных функций или все сразу:
          — нода базы данных Mnesia, тут крутиться только база
          — сервер приложения, т.е. обрабатывает запросы от клиентских приложений,
          — сервер нотификации

          Например, если нода только для БД, то ей при старте нужно узнать только любую ноду, которая хранит схему базы, чтобы войти в схему.
          Куки можно задавать при старте ноды и не нужно хранить на компьютере заранее.
  • 0
    Хочу вот вакансию на ерланговую работу разместить, а кармы не хватает :(
    • 0
      Ну-ка :)
      • 0
        habrahabr.ru/job/804/
        Большое спасибо!
        • 0
          Что-то, однако, через поиск по ключевым словам не находится
        • 0
          К тому же вакансия почти не касается Erlang'a. Написали бы Эрланг в разделе обязательно знать :)
          • 0
            Эрланга это касается напрямую, так как люди писать будут на нём. Но если я напишу его как обязательный, то людей будет ещё меньше.
            Впрочем, даже если человек не знает Эрланга и не хочет его изучать, но разбирается в основных пунктах, то он нам тоже нужен, так-то :)
            Может, мы денег мало обещаем? Просто 30-35 тысяч у нас как старт для минимума знаний, потом люди получают заметно больше, если видно, что человек растёт.
            • 0
              Понятно. Рад, что действительно на Эрланге :) Возможно, прямо так и написать в вакансии про деньги, как вы написали в этом комментарии выше. По моему 30 тыс. даже минимум для перечисленных требований маловато, а с пояснением становиться понятно.
            • 0
              Если будут писать непосредственно на нем, то не очень понятно, зачем к примеру тот же С. Имхо, если человек знает хорошо сетевой стек, особенности настройки ОСи да еще и на сях пишет еще и с опытом работы 2 года, то он наверняка уже где-то работает. И для перехода ему нужен как-то стимул… А в таком контексте даже для регионов это не очень много. Хотя если сделать на скидку на кризис, то може быть…
  • 0
    А в PATH нужно наверное C:\Program Files\erl5.6.5\binпрописывать, а не C:\Program Files\erl5.6.5\ который прописали в ERL_TOP?
    Содержимое для .erlang.cookie генерируется или сочиняется из головы? Какой длины, какие рекомендации?
    • 0
      Да, вы правы. Спасибо за замечание. Поправил в статье.
      Про .erlang.cookie: лучше сочинять самому, любую достаточно длинную последовательность случайных символов. Я не силен в криптографии, но думаю длиной не менее 16 символов или 128 бит.
      • 0
        А в документации по erlang этот вопрос как-то обсуждается? А то не ясно, что это за куки, какой может быть длины, какая длина критична для взлома и т.д.
        • 0
          Я не встречал. Немного описано здесь:
          • 0
            Ссылка съелась.
        • 0
          Я не встречал. Немного описано здесь:
          erlang.org/doc/getting_started/conc_prog.html

          У меня лично создалось впечатление, что это не серьезная защита от взлома, а просто минимально необходимая. Если вы хотите действительно надежное решение, до немного доработав Erlang можно перейти на общение нод через SSL. Поищите по этой теме в google.
      • +1
        Я обратил внимание на следующий факт.
        Если не создать файл .erlang.cookie в каталоге $HOME, то при запуске распределенной ноды (когда запускаем erl с параметром -sname или -name и именем ноды) он будет создан автоматически и куки будет сгенерирован. Алгоритм генерации куки можно найти в файле C:\Program Files\erl5.6.5\lib\kernel-2.12.5\src\auth.erl. По исходному тексту, сгенеренный куки — это строка заглавных латинских букв длинной в 20 символов.
        • 0
          Спасибо за информацию! Не знал.
          • 0
            Я сам совершенно случайно на это наткнулся. По Вашей инструкции сделал .erlang.cookie, но не знал что вписать и вписывать не стал, решил почитать литературу и потом вписать. Но по одному из туториалов, где демонстрировалось создание распределенной ноды, я запустил erl -name ping и у меня оболчка не запустилась, а выдала длинное ругательство и упала с дампом. Прочтя ругательства, я понял, что длина строки куки слишком мала, тогда я сделал поиск в исходниках по строке с ошибкой и все стало понятно.
            • 0
              Интересно, как вы пришли к открытию! :) Достойно!
  • 0
    На английском мне понравилось это введение: Thinking in Erlang, Robert Baruch
    • 0
      Да, читал. Хороший документ.
      • 0
        Еще неплохая вводная статья от Джима Ларсона (инженера из гугла): Erlang for Concurrent Programming (там можно PDF взять)
  • 0
    Я так глазами пробежался. Пара вопросов, возможно ответы были не заметил.
    Какие http сервера у вас? yaws, nginx или какая-то связка?
    Вы используете какой-то фреймворк (erlyweb например) или сами все пишете?
    И последний совсем наглый: вы в конторе серверную часть ерланговскую под виндой писали?
    • 0
      Мы используем nginx для балансировки нагрузки между серверами и работы с SSL (https). За nginx стоит mochiweb. Фрэймворки не используем. И… да, мы разрабатываем под Винду, а работает под Ubuntu :)
  • 0
    Мы используем nginx для балансировки нагрузки между серверами и работы с SSL (https). За nginx стоит mochiweb. Фрэймворки не используем. И… да, мы разрабатываем под Винду, а работает под Ubuntu :)

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