Приложение поведение
Модуль для работы с приложениями и определения обратных вызовов приложений.
Приложения — это стандартный способ упаковки программного обеспечения в Erlang/OTP. По сути, они похожи на концепцию «библиотеки», распространенную в других языках программирования, но с некоторыми дополнительными характеристиками.
Приложение — это компонент, реализующий определенную функциональность, со стандартной структурой каталогов, конфигурацией и жизненным циклом. Приложения загружаются, запускаются и останавливаются.
Файл ресурсов приложения
Приложения определяются в их файле ресурсов, который является файлом, называемым APP.app, где APP — это имя приложения. Например, файл ресурсов приложения OTP приложения ex_unit называется ex_unit.app.
Вы найдете файл ресурсов приложения в его ebin каталоге, он генерируется автоматически Mix. Некоторые его ключи взяты из списков ключевых слов, возвращаемых функциями project/0 и application/0, определенными в mix.exs, а другие генерируются самим Mix.
Подробнее о генерации файлов ресурсов приложений можно узнать в документации Mix.Tasks.Compile.App, также доступной путем выполнения mix help compile.app.
Среда приложения
Ключ env файла ресурсов приложения содержит список кортежей, которые сопоставляют атомы с терминами, и его содержимое известно как среда приложения. Обратите внимание, что эта среда не связана со средой операционной системы.
По умолчанию среда приложения — это пустой список. В проекте Mix вы можете установить этот ключ в application/0:
def application do [env: [redis_host: "localhost"]] end
и сгенерированный файл ресурсов приложения будет его включать.
Среда доступна после загрузки приложения, что подробно объяснено ниже:
Application.load(:APP_NAME) #=> :ok Application.get_env(:APP_NAME, :redis_host) #=> "localhost"
В проектах Mix среда приложения и его зависимостей может быть переопределена в файле config/config.exs. Если вы запускаете приложение с Mix, эта конфигурация доступна во время компиляции и во время выполнения, но имейте в виду, что она не включена в сгенерированный файл ресурсов приложения и не доступна, если вы запускаете приложение без Mix.
Например, кто-то, использующий ваше приложение, может переопределить его среду :redis_host следующим образом:
config :APP_NAME, redis_host: "redis.local"
Функция put_env/3 позволяет динамически настраивать среду приложения, но, как правило, каждое приложение отвечает за свою собственную среду. Не используйте функции в этом модуле для прямого доступа или изменения среды других приложений.
Среда приложения может быть переопределена с помощью опции -config модуля erl, а также с помощью командной строки, как показано ниже.
Модуль обратного вызова приложения
Ключ mod файла ресурсов приложения настраивает модуль обратного вызова приложения и аргумент запуска:
def application do
[mod: {MyApp, []}]
end Этот ключ необязателен, нужен только для приложений, которые запускают дерево надзора.
Модуль MyApp (передаваемый в :mod) должен реализовывать поведение Application. Это можно сделать, поместив use Application в этот модуль и реализовав обратный вызов start/2, например:
defmodule MyApp do
use Application
def start(_type, _args) do
children = []
Supervisor.start_link(children, strategy: :one_for_one)
end
end Обратный вызов start/2 должен создать и связать надзирателя и вернуть {:ok, pid} или {:ok, pid, state}, где pid — PID надзирателя, а state — необязательное состояние приложения. args — это второй элемент кортежа, переданного в опцию :mod.
Аргумент type , переданный в start/2, обычно :normal, за исключением распределенной установки, где конфигурируются захваты и отказы приложений. Распределенные приложения выходят за рамки этой документации.
При завершении работы приложения вызывается обратный вызов stop/1 после остановки дерева надзора runtime. Этот обратный вызов позволяет приложению выполнить любые заключительные действия по очистке. Аргумент — это состояние, возвращённое start/2, если оно было возвращено, или [] в противном случае. Возвращаемое значение stop/1 игнорируется.
Используя Application, модули получают реализацию по умолчанию для stop/1, которая игнорирует свой аргумент и возвращает :ok, но она может быть переопределена.
Модули обратного вызова приложения также могут реализовывать необязательный обратный вызов prep_stop/1. Если он присутствует, prep_stop/1 вызывается перед завершением работы дерева надзора. Его аргумент — это состояние, возвращённое start/2, если оно было возвращено, или [] в противном случае, а его возвращаемое значение передаётся в stop/1.
Жизненный цикл приложения
Загрузка приложений
Приложения загружаются, что означает, что среда runtime находит и обрабатывает их файлы ресурсов:
Application.load(:ex_unit) #=> :ok
Если приложение включает другие приложения, они также загружаются. И процедура рекурсивно повторяется, если они в свою очередь включают приложения. Включенные приложения не связаны с приложениями в проектах Mix-umbrella, это концепция Erlang/OTP, которая связана с согласованными запусками.
При загрузке приложения среда, указанная в его файле ресурсов, объединяется с любыми переопределениями из файлов конфигурации, переданных в erl через опцию -config. Стоит отметить, что релизы передают sys.config таким образом. Результирующая среда может быть повторно переопределена с помощью определённых опций -Application переданных в erl.
Загрузка приложения не загружает его модули.
На практике вы редко загружаете приложения вручную, так как это часть процесса запуска, описанного ниже.
Запуск приложений
Приложения также запускаются:
Application.start(:ex_unit) #=> :ok
После компиляции вашего приложения, запуск вашей системы — это дело запуска текущего приложения и его зависимостей. В отличие от других языков, Elixir не имеет процедуры main , которая отвечает за запуск вашей системы. Вместо этого вы запускаете одно или несколько приложений, каждое со своей логикой инициализации и завершения.
При запуске приложения среда runtime загружает его, если оно ещё не загружено (в техническом смысле, описанном выше). Затем она проверяет, уже ли запущены зависимости, перечисленные в ключе applications файла ресурсов. Наличие хотя бы одной незапущенной зависимости является ошибкой, но при запуске приложения с помощью mix run, Mix позаботится о запуске всех зависимостей за вас, поэтому на практике вам не нужно беспокоиться об этом, если только вы не запускаете приложения вручную с помощью API, предоставленного этим модулем.
Если у приложения не настроен модуль обратного вызова, запуск происходит на этом этапе. В противном случае вызывается обратный вызов start/2. PID ведущего надзирателя, возвращённый этой функцией, сохраняется средой runtime для последующего использования, а возвращённое состояние приложения также сохраняется, если оно есть.
Остановка приложений
Запущенные приложения, наконец, останавливаются:
Application.stop(:ex_unit) #=> :ok
Остановка приложения без модуля обратного вызова определена, но, за исключением некоторых системных следов, на практике это операция бездействия.
Остановка приложения с модулем обратного вызова состоит из трёх этапов:
- Если он присутствует, вызывается необязательный обратный вызов
prep_stop/1. - Завершается ведущий надзиратель.
- Вызывается обязательный обратный вызов
stop/1.
Передаваемые аргументы обратным вызовам связаны с состоянием, которое необязательно возвращается start/2, и описаны в разделе о модуле обратного вызова выше.
Важно подчеркнуть, что шаг 2 является блокирующим. Завершение работы надзирателя инициирует рекурсивную цепочку завершения дочерних элементов, поэтому все дочерние процессы завершаются в порядке. Обратный вызов stop/1 вызывается только после завершения всего дерева надзора.
Чистое завершение работы активной системы может быть выполнено вызовом System.stop/1. Он завершит работу всех приложений в обратном порядке их запуска.
По умолчанию, SIGTERM из операционной системы автоматически переводится в System.stop/0. Вы также можете получить больший контроль над сигналами ОС с помощью функции :os.set_signal/2.
Инструменты
Инструмент сборки Mix также может использоваться для запуска ваших приложений. Например, mix test автоматически запускает зависимости вашего приложения и само приложение перед выполнением ваших тестов. mix run --no-halt запускает ваш текущий проект и может использоваться для запуска долгоживущей системы. См. mix help run.
Разработчики также могут использовать инструменты, такие как Distillery, которые строят релизы. Релизы могут упаковать весь ваш исходный код, а также виртуальную машину Erlang в один каталог. Релизы также предоставляют вам явный контроль над тем, как запускается каждое приложение и в каком порядке. Они также обеспечивают более эффективный механизм для запуска и остановки систем, отладки, ведения журнала, а также мониторинга системы.
Наконец, Elixir предоставляет такие инструменты, как escripts и архивы, которые являются разными механизмами для упаковки вашего приложения. Они обычно используются, когда инструменты должны быть общими для разработчиков, а не как варианты развертывания. См. mix help archive.build и mix help escript.build для получения дополнительной информации.
Дополнительная информация
Для получения более подробной информации об приложениях, пожалуйста, обратитесь к документации модуля application Erlang и разделу Приложения руководства по принципам проектирования OTP .
Резюме
Типы
- app()
- ключ()
- тип_перезапуска()
- тип_запуска()
- состояние()
- значение()
Функции
- app_dir(приложение)
Возвращает директорию для приложения.
- app_dir(приложение, путь)
Возвращает заданный путь внутри
app_dir/1.- delete_env(приложение, ключ, опции \\ [])
Удаляет
keyиз заданнойappсреды.- ensure_all_started(приложение, тип \\ :временный)
Обеспечивает запуск данного
appи его приложений.- ensure_started(приложение, тип \\ :временный)
Обеспечивает запуск данного
app.- fetch_env(приложение, ключ)
Возвращает значение для
keyв средеappв кортеже.- fetch_env!(приложение, ключ)
Возвращает значение для
keyв средеapp.- format_error(причина)
Форматирует причину ошибки, возвращаемую функциями
start/2,ensure_started/2,stop/1,load/1иunload/1, возвращает строку.- get_all_env(приложение)
Возвращает все пары ключ-значение для
app.- get_application(модуль)
Получает приложение для заданного модуля.
- get_env(приложение, ключ, значение_по_умолчанию \\ nil)
Возвращает значение для
keyв средеapp.- load(приложение)
Загружает данное
app.- loaded_applications()
Возвращает список с информацией о загруженных приложениях.
- put_env(приложение, ключ, значение, опции \\ [])
Устанавливает
valueвkeyдля данногоapp.- spec(приложение)
Возвращает спецификацию для
app.- spec(приложение, ключ)
Возвращает значение для
keyв спецификацииapp.- start(приложение, тип \\ :временный)
Запускает данное
app.- started_applications(таймаут \\ 5000)
Возвращает список с информацией о текущих приложениях.
- stop(приложение)
Останавливает данное
app.- unload(приложение)
Выгружает данное
app.
Обработчики
- config_change(изменение, новое, удалённое)
Обработчик, вызываемый после обновления кода, если изменилась среда приложения.
- prep_stop(состояние)
Вызывается перед остановкой приложения.
- start(тип_запуска, аргументы_запуска)
Вызывается при запуске приложения.
- start_phase(фаза, тип_запуска, аргументы_фазы)
Запускает приложение в синхронных фазах.
- stop(состояние)
Вызывается после остановки приложения.
Типы
приложение()
app() :: atom()
ключ()
key() :: atom()
тип_перезапуска()
restart_type() :: :permanent | :transient | :temporary
тип_запуска()
start_type() :: :normal | {:takeover, node()} | {:failover, node()} состояние()
state() :: term()
значение()
value() :: term()
Функции
app_dir(приложение)
app_dir(app()) :: String.t()
Возвращает директорию для приложения.
Эта информация возвращается на основе пути кода. Вот пример:
File.mkdir_p!("foo/ebin")
Code.prepend_path("foo/ebin")
Application.app_dir(:foo)
#=> "foo" Даже если директория пуста и нет файла .app, она считается директорией приложения на основе имени "foo/ebin". Имя может содержать тире -, которое считается версией приложения, и оно удаляется для целей поиска:
File.mkdir_p!("bar-123/ebin")
Code.prepend_path("bar-123/ebin")
Application.app_dir(:bar)
#=> "bar-123" Для получения дополнительной информации о путях кода, обратитесь к модулю Code в Elixir и модулю :code Erlang.
app_dir(приложение, путь)
app_dir(app(), String.t() | [String.t()]) :: String.t()
Возвращает заданный путь внутри app_dir/1.
Если path является строкой, она будет использоваться как путь внутри app_dir/1. Если path является списком строк, он будет соединён (см. Path.join/1), и результат будет использован как путь внутри app_dir/1.
Примеры
File.mkdir_p!("foo/ebin")
Code.prepend_path("foo/ebin")
Application.app_dir(:foo, "my_path")
#=> "foo/my_path"
Application.app_dir(:foo, ["my", "nested", "path"])
#=> "foo/my/nested/path" delete_env(приложение, ключ, опции \\ [])
delete_env(app(), key(), timeout: timeout(), persistent: boolean()) :: :ok
Удаляет key из заданной app среды.
См. put_env/4 для описания опций.
ensure_all_started(приложение, тип \\ :временный)
ensure_all_started(app(), restart_type()) ::
{:ok, [app()]} | {:error, {app(), term()}} Обеспечивает запуск данного app и его приложений.
Аналогично start/2, но также запускает приложения, указанные в :applications в файле .app в случае, если они не были ранее запущены.
ensure_started(приложение, тип \\ :временный)
ensure_started(app(), restart_type()) :: :ok | {:error, term()} Обеспечивает запуск данного app.
Аналогично start/2, но возвращает :ok , если приложение уже запущено. Это полезно в скриптах и в настройке тестов, где приложения тестов должны быть явно запущены:
:ok = Application.ensure_started(:my_test_dep)
fetch_env(приложение, ключ)
fetch_env(app(), key()) :: {:ok, value()} | :error Возвращает значение для key в среде app в виде кортежа.
Если параметр конфигурации не существует, функция возвращает :error.
fetch_env!(приложение, ключ)
fetch_env!(app(), key()) :: value()
Возвращает значение для key в среде app.
Если параметр конфигурации не существует, возникает исключение ArgumentError.
format_error(причина)
format_error(any()) :: String.t()
Форматирует причину ошибки, возвращаемую функциями start/2, ensure_started/2, stop/1, load/1 и unload/1, возвращает строку.
get_all_env(приложение)
get_all_env(app()) :: [{key(), value()}] Возвращает все пары ключ-значение для app.
get_application(модуль)
get_application(atom()) :: atom() | nil
Получает приложение для заданного модуля.
Приложение находится путём анализа спецификации всех загруженных приложений. Возвращает nil , если модуль не указан ни в одной спецификации приложения.
get_env(приложение, ключ, значение_по_умолчанию \\ nil)
get_env(app(), key(), value()) :: value()
Возвращает значение для key в среде app.
Если параметр конфигурации не существует, функция возвращает значение default.
Примеры
get_env/3 обычно используется для чтения конфигурации ваших приложений OTP. Поскольку конфигурации Mix обычно используются для настройки приложений, мы будем использовать это в качестве иллюстрации.
Рассмотрим новое приложение :my_app. :my_app содержит движок базы данных, который поддерживает пул баз данных. Движку базы данных необходимо знать конфигурацию для каждой из этих баз данных, и эта конфигурация предоставляется парами «ключ-значение» в среде :my_app.
config :my_app, Databases.RepoOne, # A database configuration ip: "localhost", port: 5433 config :my_app, Databases.RepoTwo, # Another database configuration (for the same OTP app) ip: "localhost", port: 20717 config :my_app, my_app_databases: [Databases.RepoOne, Databases.RepoTwo]
Наш движок базы данных, используемый :my_app, должен знать, какие базы данных существуют и каковы их конфигурации. Движок базы данных может обратиться к get_env(:my_app, :my_app_databases), чтобы получить список баз данных (определённых по именам модулей). Затем наш движок базы данных может пройтись по каждому хранилищу в списке и вызвать get_env(:my_app, Databases.RepoOne) и так далее, чтобы получить конфигурацию каждого из них.
Важно: если вы пишете библиотеку для использования другими разработчиками, рекомендуется избегать использования среды приложения, так как среда приложения фактически является глобальным хранилищем. Для получения дополнительной информации ознакомьтесь с нашими руководящими принципами для библиотек.
load(app)
load(app()) :: :ok | {:error, term()} Загружает указанное app.
Для загрузки файл .app должен быть в пути загрузки. Все :included_applications также будут загружены.
Загрузка приложения не запускает его и не загружает его модули, но загружает его среду.
loaded_applications()
loaded_applications() :: [
{app(), description :: charlist(), vsn :: charlist()}
] Возвращает список с информацией о загруженных приложениях.
put_env(app, key, value, opts \\ [])
put_env(app(), key(), value(), timeout: timeout(), persistent: boolean()) :: :ok
Размещает value в key для указанного app.
Параметры
-
:timeout— таймаут изменения (по умолчанию5_000миллисекунд) -
:persistent— сохраняет заданное значение при загрузке и перезагрузке приложения
Если put_env/4 вызывается до загрузки приложения, значения среды приложения, указанные в файле .app, переопределят ранее установленные.
Параметр :persistent может быть установлен в true, когда необходимо гарантировать, что параметры, установленные этой функцией, не будут переопределены параметрами, определёнными в файле ресурса приложения при загрузке. Это означает, что постоянные значения останутся после загрузки приложения, а также при перезагрузке приложения.
spec(app)
spec(app()) :: [{key(), value()}] | nil Возвращает спецификацию для app.
Возвращаются следующие ключи:
-
:description -
:id -
:vsn -
:modules -
:maxP -
:maxT -
:registered -
:included_applications -
:applications -
:mod -
:start_phases
Обратите внимание, что среда не возвращается, так как к ней можно получить доступ с помощью fetch_env/2. Возвращает nil если приложение не загружено.
spec(app, key)
spec(app(), key()) :: value() | nil
Возвращает значение для key в спецификации app.
См. spec/1 для поддерживаемых ключей. Если заданный параметр спецификации не существует, эта функция вызовет ошибку. Возвращает nil если приложение не загружено.
start(app, type \\ :temporary)
start(app(), restart_type()) :: :ok | {:error, term()} Запускает данное app.
Если app не загружено, приложение сначала загрузится с помощью load/1. Все включённые приложения, определённые в ключе :included_applications файла .app, также будут загружены, но не будут запущены.
Кроме того, все приложения, перечисленные в ключе :applications, должны быть явно запущены до запуска этого приложения. В противном случае возвращается {:error, {:not_started, app}}, где app — имя отсутствующего приложения.
Если вы хотите автоматически загрузить и запустить все зависимости app, см. ensure_all_started/2.
Аргумент type определяет тип приложения:
-
:permanent— еслиappзавершается, все другие приложения и весь узел также завершаются. -
:transient— еслиappзавершается с причиной:normal, она сообщается, но другие приложения не завершаются. Если транзиентное приложение завершается аномально, все другие приложения и весь узел также завершаются. -
:temporary— еслиappзавершается, оно сообщается, но другие приложения не завершаются (по умолчанию).
Обратите внимание, что всегда можно явно остановить приложение, вызвав stop/1. Независимо от типа приложения, другие приложения не будут затронуты.
Также обратите внимание, что тип :transient мало пригоден на практике, так как при завершении дерева надзора причина устанавливается в :shutdown, а не в :normal.
started_applications(timeout \\ 5000)
started_applications(timeout()) :: [
{app(), description :: charlist(), vsn :: charlist()}
] Возвращает список с информацией о текущих приложениях.
stop(app)
stop(app()) :: :ok | {:error, term()} Останавливает данное app.
При остановке приложение по-прежнему загружено.
unload(app)
unload(app()) :: :ok | {:error, term()} Выгружает указанное app.
Также выгрузит все :included_applications. Обратите внимание, что функция не очищает модули приложения.
Обратные вызовы
config_change(changed, new, removed)
(необязательно)config_change(changed, new, removed) :: :ok when changed: keyword(), new: keyword(), removed: [atom()]
Вызываемый обратный вызов после обновления кода, если среда приложения изменилась.
changed — список ключевых слов с ключами и изменёнными значениями в среде приложения. new — список ключевых слов со всеми новыми ключами и их значениями. removed — список всех удалённых ключей.
prep_stop(state)
(необязательно)prep_stop(state()) :: state()
Вызывается перед остановкой приложения.
Эта функция вызывается перед завершением супервизора верхнего уровня. Она получает состояние, возвращённое start/2, если оно было, или [] в противном случае. Возвращаемое значение позже передаётся в stop/1.
start(start_type, start_args)
start(start_type(), start_args :: term()) ::
{:ok, pid()} | {:ok, pid(), state()} | {:error, reason :: term()} Вызывается при запуске приложения.
Эта функция вызывается при запуске приложения с помощью Application.start/2 (и функций, которые на ней основаны, таких как Application.ensure_started/2). Эта функция должна запускать процесс верхнего уровня приложения (который должен быть верхним супервизором дерева надзора приложения, если приложение следует принципам проектирования OTP в отношении надзора).
start_type определяет, как запускается приложение:
-
:normal— используется, если запуск является обычным запуском или если приложение распределено и запущено на текущем узле из-за восстановления с отказавшего узла, а ключ спецификации приложения:start_phasesравен:undefined. -
{:takeover, node}— используется, если приложение распределено и запущено на текущем узле из-за восстановления с отказавшего узлаnode. -
{:failover, node}— используется, если приложение распределено и запущено на текущем узле из-за восстановления с отказавшего узлаnode, а ключ спецификации приложения:start_phasesне равен:undefined.
start_args — аргументы, передаваемые приложению в ключе спецификации :mod (например, mod: {MyApp, [:my_args]}).
Эта функция должна возвращать {:ok, pid} или {:ok, pid, state} в случае успешного запуска. pid должен быть PID верхнего супервизора. state может быть произвольной структурой данных, и если она опущена, по умолчанию будет []; если приложение позже останавливается, state передаётся обратно в обратный вызов stop/1 (см. документацию для обратного вызова stop/1 для получения дополнительной информации).
use Application не предоставляет реализацию по умолчанию для обратного вызова start/2.
start_phase(phase, start_type, phase_args)
(необязательно)start_phase(phase :: term(), start_type(), phase_args :: term()) ::
:ok | {:error, reason :: term()} Запускает приложение в синхронных фазах.
Эта функция вызывается после завершения start/2, но до возвращения Application.start/2. Она будет вызвана один раз для каждой фазы запуска, определённой в спецификации приложения (и всех включённых приложений), в порядке их перечисления.
stop(state)
stop(state()) :: term()
Вызывается после остановки приложения.
Эта функция вызывается после остановки приложения, т. е. после остановки его дерева надзора. Она должна делать обратное тому, что делал обратный вызов start/2, и должна выполнять необходимые действия по очистке. Возвращаемое значение этого обратного вызова игнорируется.
state — это состояние, возвращаемое start/2, если оно было, или [] в противном случае. Если присутствует необязательный обратный вызов prep_stop/1, то state — это значение, возвращаемое им вместо этого.
use Application определяет реализацию по умолчанию этой функции, которая ничего не делает и просто возвращает :ok.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.8.2/Application.html