Приложение поведение
Модуль для работы с приложениями и определения обратных вызовов приложений.
В Elixir (на самом деле, в Erlang/OTP), приложение — это компонент, реализующий определенную функциональность, который может быть запущен и остановлен как единица и может быть повторно использован в других системах.
Приложения определяются с помощью файла приложения, названного APP.app, где APP — имя приложения, обычно в underscore_case. Файл приложения должен находиться в том же каталоге ebin, что и скомпилированные модули приложения. В Elixir, инструмент сборки Mix отвечает за компиляцию исходного кода и генерацию файла приложения .app. Вы можете узнать больше о генерации файлов .app, набрав mix help compile.app.
После компиляции приложения, запуск вашей системы сводится к запуску текущего приложения и его зависимостей. В отличие от других языков, в Elixir нет процедуры main, которая отвечает за запуск вашей системы. Вместо этого вы запускаете одно или несколько приложений, каждое со своей собственной логикой инициализации и завершения.
Запуск приложения выполняется с помощью «обратного вызова модуля приложения», который представляет собой модуль, определяющий функцию start/2. Функция start/2 должна затем запустить супервайзера, который часто называется супервайзером верхнего уровня, поскольку он находится в корне потенциально длинного дерева супервизии. При завершении работы системы все приложения завершают работу своего супервайзера верхнего уровня, который завершает дочерние элементы в обратном порядке их запуска.
Мы упоминали, что инструмент сборки Mix отвечает за компиляцию приложений, но он также способен запускать приложения. Например, mix test автоматически запускает зависимости вашего приложения и само приложение перед выполнением тестов. mix run --no-halt также загружает ваш текущий проект и может использоваться для запуска долговременной системы. См. mix help run.
Разработчики также могут использовать инструменты, такие как Distillery, которые создают релизы. Релизы могут упаковать весь ваш исходный код, а также виртуальную машину Erlang в один каталог. Релизы также предоставляют вам явный контроль над тем, как и в каком порядке запускается каждое приложение. Они также обеспечивают более упрощенный механизм запуска и остановки систем, отладки, ведения журналов и мониторинга систем.
Наконец, Elixir предоставляет инструменты, такие как escripts и архивы, которые представляют собой различные механизмы для упаковки вашего приложения. Они обычно используются, когда инструменты должны быть совместно использованы между разработчиками, а не как варианты развертывания. Подробнее см. mix help archive.build и mix help escript.build.
Чистое завершение работы живой системы можно выполнить, вызвав System.stop/1. Это приведет к остановке всех приложений в обратном порядке их запуска. Каждое приложение затем остановит своего супервайзера верхнего уровня, если он доступен, что затем остановит его дочерние элементы.
Начиная с Erlang/OTP 19.1, SIGTERM из операционной системы будет автоматически переведен в System.stop/0. Erlang/OTP 20 предоставляет пользователю больший контроль над сигналами ОС через функцию :os.set_signal/2.
Приложения также предоставляют «среду приложения», которая определяет, как настраиваются приложения. Среда приложения может быть настроена статически, с помощью файла конфигурации, или динамически через put_env/3 и другие функции.
В следующих разделах мы более подробно рассмотрим «среду приложения» и «обратный вызов модуля приложения».
Среда приложения
После запуска приложения OTP предоставляет среду приложения, которую можно использовать для настройки приложения.
Предполагая, что вы находитесь внутри проекта Mix, вы можете изменить функцию application/0 в файле mix.exs следующим образом:
def application do [env: [hello: :world]] end
В функции приложения мы можем определить значения по умолчанию для среды нашего приложения. Запустив ваше приложение с помощью iex -S mix, вы можете получить доступ к значению по умолчанию:
Application.get_env(:APP_NAME, :hello) #=> :world
Приложения и зависимости в проектах Mix обычно настраиваются через файл config/config.exs. Например, кто-то, использующий ваше приложение, может настроить ключ :hello следующим образом:
config :APP_NAME, hello: :brand_new_world
Также можно настроить приложения динамически с помощью put_env/3.
Обратите внимание, что каждое приложение отвечает за свою среду. Не используйте функции этого модуля для прямого доступа к или изменения среды других приложений (это может привести к несогласованным данным в среде приложения).
Обратный вызов модуля приложения
Зачастую приложение определяет дерево супервизии, которое должно быть запущено и остановлено при запуске и остановке приложения. Для этого нам нужно определить обратный вызов модуля приложения. Первый шаг — определить обратный вызов модуля в определении приложения в файле mix.exs.
def application do
[mod: {MyApp, []}]
end Теперь наше приложение требует модуля MyApp для предоставления обратного вызова приложения. Это можно сделать, вызвав use Application в этом модуле и определив обратный вызов start/2, например:
defmodule MyApp do
use Application
def start(_type, _args) do
MyApp.Supervisor.start_link()
end
end start/2 обычно возвращает {:ok, pid} или {:ok, pid, state}, где pid идентифицирует дерево супервизии, а state — состояние приложения. args — второй элемент кортежа, переданного опции :mod.
Аргумент type, передаваемый в start/2, обычно является :normal, за исключением распределенной настройки, где настраиваются перехваты и отказоустойчивость приложений. Распределенные приложения выходят за рамки этой документации. Для тех, кто заинтересован в этой теме, обратитесь к документации OTP:
Разработчик также может реализовать обратный вызов stop/1 (автоматически определенный use Application), который выполняет очистку приложения. Он получает состояние приложения и может вернуть любое значение. Обратите внимание, что завершение работы супервайзера автоматически обрабатывается виртуальной машиной.
Приложение без дерева супервизии не определяет обратный вызов модуля приложения в файле определения приложения в mix.exs файле. Несмотря на отсутствие модуля с обратными вызовами приложения, такими как start/2 и stop/1, приложение может быть запущено и остановлено так же, как приложение с деревом супервизии.
Обзор
Типы
- app()
- key()
- start_type()
- state()
- value()
Функции
- app_dir(app)
-
Получение каталога для приложения
- app_dir(app, path)
-
Возвращает заданный путь внутри
app_dir/1 - delete_env(app, key, opts \\ [])
-
Удаляет
keyиз заданнойappсреды - ensure_all_started(app, type \\ :temporary)
-
Обеспечивает запуск заданного
appи его приложений - ensure_started(app, type \\ :temporary)
-
Обеспечивает запуск заданного
app - fetch_env(app, key)
-
Возвращает значение для
keyв средеappв виде кортежа - fetch_env!(app, key)
-
Возвращает значение для
keyв средеapp - format_error(reason)
-
Форматирует причину ошибки, возвращаемую
start/2,ensure_started/2,stop/1,load/1иunload/1, возвращает строку - get_all_env(app)
-
Возвращает все пары ключ-значение для
app - get_application(module)
-
Получает приложение для данного модуля
- get_env(app, key, default \\ nil)
-
Возвращает значение для
keyв средеapp - load(app)
-
Загружает данное
app - loaded_applications()
-
Возвращает список с информацией о загруженных приложениях
- put_env(app, key, value, opts \\ [])
-
Устанавливает
valueвkeyдля данногоapp - spec(app)
-
Возвращает спецификацию для
app - spec(app, key)
-
Возвращает значение для
keyв спецификацииapp - start(app, type \\ :temporary)
-
Запускает данное
app - started_applications(timeout \\ 5000)
-
Возвращает список с информацией о приложениях, которые в настоящее время выполняются
- stop(app)
-
Останавливает данное
app - unload(app)
-
Выгружает данное
app
Callbacks
- start(start_type, start_args)
-
Вызывается при запуске приложения
- start_phase(phase, start_type, phase_args)
-
Запуск приложения в синхронных фазах
- stop(state)
-
Вызывается при остановке приложения
Типы
app()
app() :: atom()
key()
key() :: atom()
start_type()
start_type() :: :permanent | :transient | :temporary
state()
state() :: term()
value()
value() :: term()
Функции
app_dir(app)
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, path)
app_dir(app(), String.t() | [String.t()]) :: String.t()
Возвращает заданный путь внутри app_dir/1.
delete_env(app, key, opts \\ [])
delete_env(app(), key(), [timeout: timeout(), persistent: boolean()]) :: :ok
Удаляет key из заданной app среды.
См. put_env/4 для описания опций.
ensure_all_started(app, type \\ :temporary)
ensure_all_started(app(), start_type()) ::
{:ok, [app()]} |
{:error, {app(), term()}} Обеспечивает запуск заданного app и его приложений.
Аналогично start/2, но также запускает приложения, перечисленные в :applications в файле .app, если они ранее не запускались.
ensure_started(app, type \\ :temporary)
ensure_started(app(), start_type()) :: :ok | {:error, term()} Обеспечивает запуск заданного app.
Аналогично start/2, но возвращает :ok , если приложение уже запущено. Это полезно в скриптах и настройке тестов, где тестовые приложения необходимо явно запускать:
:ok = Application.ensure_started(:my_test_dep)
fetch_env(app, key)
fetch_env(app(), key()) :: {:ok, value()} | :error Возвращает значение для key в среде app в виде кортежа.
Если параметр конфигурации не существует, функция возвращает :error.
fetch_env!(app, key)
fetch_env!(app(), key()) :: value() | no_return()
Возвращает значение для key в среде app.
Если параметр конфигурации не существует, генерирует исключение ArgumentError.
format_error(reason)
format_error(any()) :: String.t()
Форматирует причину ошибки, возвращаемую start/2, ensure_started/2, stop/1, load/1 и unload/1, возвращает строку.
get_all_env(app)
get_all_env(app()) :: [{key(), value()}] Возвращает все пары ключ-значение для app.
get_application(module)
get_application(atom()) :: atom() | nil
Получает приложение для данного модуля.
Приложение находится путем анализа спецификаций всех загруженных приложений. Возвращает nil если модуль не указан в спецификации ни одного приложения.
get_env(app, key, default \\ nil)
get_env(app(), key(), value()) :: value()
Возвращает значение для key в среде app.
Если параметр конфигурации не существует, функция возвращает значение default.
load(app)
load(app()) :: :ok | {:error, term()} Загружает данное app.
Для загрузки необходим файл .app в путях загрузки. Также будут загружены все :included_applications.
Загрузка приложения не запускает его и не загружает его модули, но загружает его среду.
loaded_applications()
loaded_applications() :: [tuple()]
Возвращает список с информацией о загруженных приложениях.
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, когда необходимо гарантировать, что параметры, установленные с помощью этой функции, не будут переопределяться параметрами, определенными в файле ресурсов приложения при загрузке. Это означает, что значения persistent останутся после загрузки приложения, а также при повторной загрузке приложения.
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(), start_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()) :: [tuple()]
Возвращает список с информацией о приложениях, которые в настоящее время работают.
stop(app)
stop(app()) :: :ok | {:error, term()} Останавливает заданное app.
После остановки приложение по-прежнему загружено.
unload(app)
unload(app()) :: :ok | {:error, term()} Выгружает заданное app.
Он также выгрузит все :included_applications. Обратите внимание, что функция не очищает модули приложения.
Обратные вызовы
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) (optional)
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, если модуль приложения определяет такую функцию.
use Application определяет реализацию по умолчанию этой функции, которая ничего не делает и просто возвращает :ok.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.5.3/Application.html