Pull to refresh

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

Erlang/OTP *
Данная статья является продолжением цикла статей про язык программирования Эрланг/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 с примерами использования этих технологий на серверах Рисоваськи.
Tags:
Hubs:
Total votes 47: ↑43 and ↓4 +39
Views 13K
Comments Comments 41