Распределённые приложения
| Оригинал этой статьи находится по адресу Distributed Applications |
Перевод: К. Заборский
Дата: 15.11.2006
Версия: 1.0
Содержание |
Распределённые приложения
Определение
В системе, распределённой между несколькими нодами Эрланга, может понадобиться распределённое управление приложениями. Если нода, где работает определённое приложение, отключается, приложение должно быть перезапущено на другой ноде.
Такое приложение называется распределённым приложением. Следует заметить, что распределённым является управление приложением, все же приложения могут быть распределёнными в том смысле, что они, к примеру, используют сервисы на других нодах.
Т.к. распределённое приложение может "перемещаться" между нодами, некий механизм адресации нужен для того, чтобы к этому приложению могли обращаться другие приложения, независимо от того, на какой ноде это приложение выполняется в данный момент. Этот момент не освещён здесь, но модуль global приложения Kernel или модуль pg приложения STDLIB могут быть использованы для этой цели.
Спецификация распределённых приложений
Распределённые приложения управляются контроллером приложений и процессом распределённого контроллера приложений dist_ac. Оба эти процесса являются частью приложения kernel. Таким образом, распределённые приложения указываются при конфигурировании приложения kernel, используя следующий параметр настройки (см. также kernel(6)):
distributed = [{Application, [Timeout,] NodeDesc}]
- Указывает где приложение Application = atom() может исполняться. Переменная NodeDesc = [Node | {Node,...,Node}] является списком имён нод в порядке приоритета. Порядок следования нод в кортеже не определён. Переменная Timeout = integer() задаёт интервал времени, в милисекундах, прежде, чем приложение перезапустится на другом узле. Значение по умолчанию - 0.
Для того, чтобы распределённое управление приложениями работало корректно, ноды, где может выполняться распределённое приложение, должны иметь возможнось обмениваться данными друг с другом и выбирать ту ноду, где должно быть запущено приложение. Это происходит с использованием следующих параметров приложения kernel:
sync_nodes_mandatory = [Node]
- Устанавливает, какие другие узлы должны быть запущены (с учётом таймаута, указанного в параметре sync_nodes_timeout).
sync_nodes_optional = [Node]
- Устанавливает, какие другие узлы могут быть запущены (с учётом таймаута, указанного в параметре sync_nodes_timeout).
sync_nodes_timeout = integer() | infinity
- Устанавливает, сколько миллисекунд необходимо ожидать запуска других нод.
После запуска нода будет ожидать запуска всех остальных узлов, указанных в списках sync_nodes_mandatory и sync_nodes_optional. Когда запущены все ноды или только обязательные ноды и истёк таймаут, указанный в sync_nodes_timeout, будут запущены все приложения. Если не все обязательные ноды стартовали, данная нода завершит работу.
Пример: Приложение myapp должно быть запущено на ноде cp1@cave. Если эта нода отключается, данное приложение должно быть перезапущено на cp2@cave или cp3@cave. Файл системной конфигурации cp1.config для узла cp1@cave может выглядеть следующим образом:
[{kernel,
[{distributed, [{myapp, 5000, [cp1@cave, {cp2@cave, cp3@cave}]}]},
{sync_nodes_mandatory, [cp2@cave, cp3@cave]},
{sync_nodes_timeout, 5000}
]
}
].
Файлы системной конфигурации для нод cp2@cave и cp3@cave идентичны, за исключением списка обязательных нод, которые должны иметь значение [cp1@cave, cp3@cave] для cp2@cave и значение [cp1@cave, cp2@cave] для cp3@cave.
Примечание
Все задействованные ноды должны иметь одинаковое значение параметра distributed и параметра sync_nodes_timeout, иначе поведение системы не определено.
Запуск и остановка распределённых приложений
Когда все задействованные ноды (обязательные) запущены, распределённое приложение может быть запущено при помощи вызова application:start(Application) на всех этих нодах.
Конечно, можно использовать boot-скрипт (см. Релизы), который автоматически запускает приложение.
Приложение будет запущено на первой ноде, указанной в параметре конфигурации distributed, и которая находится в рабочем состоянии. Приложение запускается в обычном режиме. То есть: создаётся владелец приложения и вызывает функцию обратного вызова приложения:
Module:start(normal, StartArgs)
Пример: Продолжая пример из предыдущего раздела, три ноды запускается, согласно файлу конфигурации системы :
> erl -sname cp1 -config cp1 > erl -sname cp2 -config cp2 > erl -sname cp3 -config cp3
Когда все ноды приходят в рабочее состояние, приложение myapp может быть запущено. Это происходит при помощи вызова application:start(myapp) на всех трёх нодах. Затем оно запускается на cp1, как это показано на приведённом ниже рисунке.
Подобным же образом приложение должно быть остановлено при помощи вызова application:stop(Application) на всех задействованных нодах.
Восстановление после сбоя
Если нода, где выполняется приложение, отключается, приложение перезапускается (после истечения заданного таймаута) на первой ноде, указанной в параметре конфигурации, и которая находится в рабочем состоянии. Это называется восстановлением после сбоя.
Приложение запускается нормальным образом на новой ноде, то есть, владелец приложения вызывает функцию:
Module:start(normal, StartArgs)
Исключение: Если для приложение задан ключ start_phases (см. Включаемые приложения), то оно запускается при помощи вызова функции:
Module:start({failover, Node}, StartArgs)
где Node - это отключившаяся нода.
Пример: Если cp1 отключается, система проверяет, на которой из нод cp2 или cp3, меньше всего запущено приложений, но ждёт 5 секунд перезапуска ноды cp1. Если cp1 не перезапускается и на cp2 выполняется меньше приложений, чем на cp3, то приложение myapp перезапускается на cp2.
Предположим, что теперь cp2 таже отключается и не перезапускается в течение 5 секунд. myapp теперь будет перезапущено на cp3.
Передача управления
Если запускается нода, у которой больший приоритет в параметре distributed, чем нода, где выполняется распределённое приложение в данный момент, приложение будет перезапущено на новой ноде и остановлено на старой. Это называется передачей управления.
Приложение запускается владельцем приложения при помощи вызова:
Module:start({takeover, Node}, StartArgs)
где Node - это старая нода.
Пример: Если myapp выполняется на cp3, и, если теперь перезапускается cp2, это не перезапустит приложение myapp, потому что порядок между нодами cp2 и cp3 не определён.
Однако, если также перезапускается cp1, функция application:takeover/2 переместит приложение на ноду cp1, потому как у cp1 более высокий приоритет, чем у cp3 для этого приложения. В этом случае функция Module:start({takeover, cp3@cave}, StartArgs) будет вызвана на cp1 для запуска приложения.






