Приложения OTP
| Оригинал этой статьи находится по адресу Applications |
Перевод: К. Заборский
Дата: 12.11.2006
Версия: 1.0
Эту главу стоит читать вместе с app(4) и application(3).
Содержание |
Концепция приложений
Когда у нас есть написанный код, реализующий какую-то специфическую функциональность, возможно мы захотим сделать из этого кода приложение - компонент, который можно запускать и останавливать как единое целое, и который также может быть использован повторно в других системах.
Чтобы сделать это, мы создаём модуль обратного вызова для приложения, где мы описываем, как это приложение следует запускать и останавливать.
Затем, нужна спецификация приложения, которую помещают в файл ресурсов приложения. Кроме того, надо указать из каких модулей состоит приложение и имя модуля обратного вызова.
Если использовать systools, утилиты Erlang/OTP для организации кода (см. Релизы), код каждого приложения помещают в отдельную папку в соответствии с предопределённой структурой каталогов.
Модуль обратного вызова для приложения
Как запустить и остановить код приложения, т.е. его дерево надзора, описывается двумя функциями обратного вызова:
start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State}
stop(State)
Функция start вызывается для запуска приложения, она должна создать дерево надзора при помощи запуска супервайзора высшего уровня. Она должна вернуть pid супервайзора высшего уровня и опциональный терм State, который по умолчанию есть пустой список []. Этот терм передаётся "as-is" функции stop.
Параметр StartType обычно содержит атом normal. Он содержит другие значения только в случае передачи управления и восстановления после сбоя, см. Распределённые приложения (Distributed Applications). Параметр StartArgs определяется ключом mod в файле ресурсов приложения.
Функция stop/1 вызывается после того, как приложение было остановлено и необходимо выполнить необходимую очистку после этого. Следует отметить, что непосредственная остановка приложения, которая состоит в остановке дерева контроля, обрабатывается автоматически, как это описано в части о запуске и остановке приложений.
Пример модуля обратного вызова приложения, содержащего дерево надзора из главы о поведении супервайзора:
-module(ch_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_Type, _Args) ->
ch_sup:start_link().
stop(_State) ->
ok.
Библиотечное приложение, которое нельзя запускать или останавливать, не должно содержать модуль обратного вызова приложения.
Файл ресурсов приложения
Чтобы определить приложение, мы создаём спецификацию приложения, которая помещается в файл ресурсов приложения, или, короче, в .app-файл:
{application, Application, [Opt1,...,OptN]}.
Application - это атом, являющийся именем приложения. Файл должен называться Application.app.
Каждая переменная Opt - это тупл вида {Key, Value}, который определяет определённое свойство приложения. Все ключи опциональны. Для неуказанных ключей используются значения по умолчанию.
Содержимое минимального .app-файла для библиотечного приложения libapp выглядит следующим образом:
{application, libapp, []}.
Содержимое минимального .app-файла ch_app.app для приложения дерева надзора ch_app выглядит как:
{application, ch_app,
[{mod, {ch_app,[]}}]}.
Ключ mod определяет модуль обратного вызова и начальный аргумент приложения, в этом случае ch_app и [] соответственно. Это значит, что
ch_app:start(normal, [])
будет вызвано, когда должно быть запущено приложение, и
ch_app:stop([])
будет вызвано, когда приложение должно быть остановлено.
При использовании systools, утилит Erlang/OTP для упаковки ??? кода (см. Releases), ключи description, vsn, modules, registered и applications должны быть указаны:
{application, ch_app,
[{description, "Channel allocator"},
{vsn, "1"},
{modules, [ch_app, ch_sup, ch3]},
{registered, [ch3]},
{applications, [kernel, stdlib, sasl]},
{mod, {ch_app,[]}}
]}.
description
- Короткое описание, строка. Значение по умолчанию - "".
vsn
- Номер версии, строка. Значение по умолчанию - "".
modules
- Все модули, привносимые приложением. systools использует этот список для генерации boot-скриптов и tar-файлов. Модуль должен быть определён в одном и только одном приложении. Значение по умолчанию - [].
registered
- Все имена зарегистрированных в приложении процессов. systools использует этот список для обнаружения конфликтов имён между приложениями. Значение по умолчанию - [].
applications
- Все приложения, которые должны быть запущены до запуска этого приложения. systools использует этот список для создания корректных boot-скриптов. Значение по умолчанию - [], но заметьте, что все приложения зависят, как минимум, от kernel и stdlib.
Синтаксис и содержимое файла ресурсов приложения детально описаны в app(4).
Структура каталогов
При организации кода с использованием systools, код каждого приложения помещается в отдельный каталог lib/Application-Vsn, где Vsn - номер версии.
Также полезно знать, что даже когда systools не используется, т.к. Erlang/OTP сам организовн с принципами OTP и таким образом организован в виде такой структуры каталогов. Код-сервер (см. code(3)) автамотически использует код из каталога с наибольшим номером версии, в том случае, если имеется более одной версии приложения.
Структура каталогов приложения, конечно, также может быть использована и в окружении разработки. В таком случае номер версии в имени может быть опущен.
Каталог приложения содержит следующие подкаталоги:
- src
- ebin
- priv
- include
src
- Содержит исходный код на Erlang.
ebin
- Содержит код обьектов Erlang'а, beam-файлы. .app-файл также помещается сюда.
priv
- Используется для файлов, специфичных для приложения. К примеру, исполняемые файлы на C помещают сюда. Для доступа к этому каталогу следует использовать функцию code:priv_dir/1.
include
- Используется для include-файлов ???.
Контроллер приложений
Когда запускается рантайм система Erlang'а, определённое число процессов запускаются как часть приложения Kernel. Одним из таких процессов является процесс контроллера приложений, зарегистрированный как application_controller.
Все операции над приложениями координируются контроллером приложений. Обращения к нему идут при помощи функций в модуле application, см. application(3). В частности, приложения могут быть загружены, выгружены, запущены и остановлены.
Загрузка и выгрузка приложений
Перед тем, как приложение сможет быть запущено, оно должно быть загружено. Контроллер приложений читает и запоминает информацию из .app-файла.
1> application:load(ch_app).
ok
2> application:loaded_applications().
[{kernel,"ERTS CXC 138 10","2.8.1.3"},
{stdlib,"ERTS CXC 138 10","1.11.4.3"},
{ch_app,"Channel allocator","1"}]
Приложение, которое было остановлено или никогда не было запущено, может быть выгружено. Информации о приложении стирается из внутренней базы данных контроллера приложений.
3> application:unload(ch_app).
ok
4> application:loaded_applications().
[{kernel,"ERTS CXC 138 10","2.8.1.3"},
{stdlib,"ERTS CXC 138 10","1.11.4.3"}]
Примечание
Загрузка/выгрузка приложения не загружает/выгружает код, используемый приложением. Загрузка кода осуществляется обычным способом.
Запуск и остановка приложений
Приложение запускается при помощи вызова:
5> application:start(ch_app).
ok
6> application:which_applications().
[{kernel,"ERTS CXC 138 10","2.8.1.3"},
{stdlib,"ERTS CXC 138 10","1.11.4.3"},
{ch_app,"Channel allocator","1"}]
Если приложение ещё не загружено, контроллер приложений сначала загружает его, используя application:load/1. Он проверяет значение ключа applications, удостоверяясь, что все приложения, которые должны быть запущены до запуска приложения, выполняются.
Затем контроллер приложений создаёт "владельца" приложения для приложения. "Владелец" приложения - это групповой лидер ??? всех процессов в приложении. "Владелец" приложения запускает приложение, вызывая функцию обратного вызова приложения start/2 в его модуле, передавая параметр запуска, указанный в ключе mod в .app-файле.
Приложение останавливается, но не выгружается, при помощи вызова:
7> application:stop(ch_app). ok
"Владелец" приложения останавливает приложение, посылая сигнал завершения работы супервайзору высшего уровня. Супервайзор высшего уровня посылает этот сигнал всем своим дочерним процессам, и всё дерево останавливается в порядке, обратном порядку запуска. "Владелец" приложения затем вызывает функцию обратного вызова приложения stop/1 в модуле, указанном в ключе mod.
Конфигурирование приложения
Приложение может быть сконфигурировано при помощи параметров конфигурации. Они представляют собой список туплов вида {Par, Val}, указанный в ключе key env в .app-файле.
{application, ch_app,
[{description, "Channel allocator"},
{vsn, "1"},
{modules, [ch_app, ch_sup, ch3]},
{registered, [ch3]},
{applications, [kernel, stdlib, sasl]},
{mod, {ch_app,[]}},
{env, [{file, "/usr/local/log"}]}
]}.
Переменная Par должна быть атомом, Val - любой терм. Приложение может получить значение параметра конфигурации при помощи вызова application:get_env(App, Par) или ряда схожих функций, см. application(3).
Пример:
% erl
Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
Eshell V5.2.3.6 (abort with ^G)
1> application:start(ch_app).
ok
2> application:get_env(ch_app, file).
{ok,"/usr/local/log"}
Значения в .app-файле могут быть перекрыты значениями в файле конфигурации системы. Этот файл содержит параметры конфигурации значимых приложений:
[{Application1, [{Par11,Val11},...]},
...,
{ApplicationN, [{ParN1,ValN1},...]}].
Файл конфигурации системы должен иметь название Name.config и Erlang должен быть запущен с указанием аргумента коммандной строки -config Name. См. config(4) для получения более подробной информации.
Пример: Создан файл test.config следующего содержания:
[{ch_app, [{file, "testlog"}]}].
Значиние ключа file перекроет значение ключа file, определённое в .app-файле:
% erl -config test
Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
Eshell V5.2.3.6 (abort with ^G)
1> application:start(ch_app).
ok
2> application:get_env(ch_app, file).
{ok,"testlog"}
Если используется работа с релизами, то только один файл конфигурации системы должен быть исопльзован, и этот файл обязан нызываться sys.config.
Значения в .app-файле, также как значния в файле конфигурации системы, могут быть перекрыты непосредственно из коммандной строки:
% erl -ApplName Par1 Val1 ... ParN ValN
Пример:
% erl -ch_app file '"testlog"' Erlang (BEAM) emulator version 5.2.3.6 [hipe] [threads:0]
Eshell V5.2.3.6 (abort with ^G)
1> application:start(ch_app).
ok
2> application:get_env(ch_app, file).
{ok,"testlog"}
Типы запусков приложения
Тип запуска определяется при его запуске:
application:start(Application, Type)
Вызов application:start(Application) представляет собой то же самое, что и вызов application:start(Application, temporary). Тип может быть также permanent или transient:
- Если приложение permanent завершается, все другие приложения и рантайм система также завершаются.
- Если приложение transient завершается с кодом завершения normal, посылается сигнал об этом, но никакие другие приложения не завершаются. Если приложение transient завершается черезвычайно, т.е. с любым кодом завершения помимо normal, все другие приложения и рантайм система также завершаются.
- Если завершается приложение temporary, посылается сигнал об этом, но никакие другие приложения не завершаются.
В любой момент можно явным образом завершить приложение при помощи вызова application:stop/1. Несмотря на тип запуска, никакие другие приложения не будут затронуты.
Следует заметить, что тип transient является мало полезным с практической точки зрения, т.к., когда завершается дерево надзора, код завершения имеет значение shutdown, а не normal.

