Приложение поведение
Модуль для работы с приложениями и определения обратных вызовов приложений.
Приложения — это стандартный способ упаковки программного обеспечения в Erlang/OTP. По сути, они аналогичны концепции «библиотеки», распространённой в других языках программирования, но с некоторыми дополнительными характеристиками.
Приложение — это компонент, реализующий определённую функциональность, со стандартной структурой каталогов, конфигурацией и жизненным циклом. Приложения загружаются, запускаются и останавливаются.
Файл ресурсов приложения
Приложения описываются в своём файле ресурсов, который называется APP.app, где APP — имя приложения. Например, файл ресурсов приложения 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 после остановки дерева наблюдения временем выполнения. Этот обратный вызов позволяет приложению выполнить любые заключительные действия по очистке. Аргументом является состояние, возвращённое start/2, если таковое было, или [] в противном случае. Значение возврата stop/1 игнорируется.
Используя Application, модули получают реализацию по умолчанию для stop/1, которая игнорирует свой аргумент и возвращает :ok, но она может быть переопределена.
Модули обратного вызова приложений также могут реализовывать необязательный обратный вызов prep_stop/1. Если он присутствует, prep_stop/1 вызывается перед завершением дерева наблюдения. Его аргументом является состояние, возвращённое start/2, если таковое было, или [] в противном случае, а его возвращаемое значение передаётся в stop/1.
Жизненный цикл приложения
Загрузка приложений
Приложения загружаются, что означает, что время выполнения находит и обрабатывает их файлы ресурсов:
Application.load(:ex_unit) #=> :ok
Если приложение включает другие приложения, они также загружаются. И процедура рекурсивно повторяется, если они в свою очередь включают приложения. Включенные приложения не связаны с приложениями в проектах Mix, это концепция Erlang/OTP, связанная с согласованным запуском.
При загрузке приложения среда, указанная в его файле ресурсов, объединяется с любыми переопределениями из файлов конфигурации, переданных в erl с помощью опции -config. Стоит отметить, что релизы передаются таким образом. Полученную среду можно переопределить ещё раз с помощью определённых опций -Application , переданных в erl.
Загрузка приложения не загружает его модули.
На практике вы редко загружаете приложения вручную, потому что это часть процесса запуска, описанного ниже.
Запуск приложений
Приложения также запускаются:
Application.start(:ex_unit) #=> :ok
После компиляции вашего приложения, запуск вашей системы заключается в запуске текущего приложения и его зависимостей. В отличие от других языков, Elixir не имеет процедуры main, которая отвечает за запуск вашей системы. Вместо этого вы запускаете одно или несколько приложений, каждое со своей собственной логикой инициализации и завершения.
При запуске приложения время выполнения загружает его, если оно ещё не было загружено (в техническом смысле, описанном выше). Затем оно проверяет, уже ли запущены зависимости, указанные в ключе applications файла ресурсов. Наличие хотя бы одной не запущенной зависимости — это ошибка, но когда вы запускаете приложение с помощью mix run, Mix позаботится о запуске всех зависимостей за вас, поэтому на практике вам не нужно об этом беспокоиться, если вы не запускаете приложения вручную с помощью API, предоставляемого этим модулем.
Если у приложения не настроен модуль обратного вызова, запуск выполняется на этом этапе. В противном случае вызывается его обратный вызов start/2. PID наблюдателя верхнего уровня, возвращённый этой функцией, сохраняется временем выполнения для последующего использования, а возвращённое состояние приложения также сохраняется, если таковое имеется.
Остановка приложений
Запущенные приложения, наконец, останавливаются:
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 предоставляет инструменты, такие как скрипты и архивы, которые представляют собой разные механизмы упаковки вашего приложения. Обычно они используются, когда инструменты должны быть общими для разработчиков, а не в качестве вариантов развертывания. См. mix help archive.build и mix help escript.build для получения более подробной информации.
Дополнительная информация
Для получения более подробной информации об приложениях, пожалуйста, обратитесь к документации модуля application Erlang и разделу Приложения в Руководстве пользователя по принципам проектирования OTP.
Резюме
Типы
Функции
- app_dir(app)
Получает директорию для 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_all_env(config, opts \\ [])
Одновременно помещает среду для нескольких приложений.
- 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.
Обработчики
- config_change(changed, new, removed)
Обработчик, вызываемый после обновления кода, если среда приложения изменилась.
- prep_stop(state)
Вызывается перед остановкой приложения.
- start(start_type, start_args)
Вызывается при запуске приложения.
- start_phase(phase, start_type, phase_args)
Запускает приложение в синхронных фазах.
- stop(state)
Вызывается после остановки приложения.
Типы
app()
Specs
app() :: atom()
application_key()
Specs
application_key() :: :start_phases | :mod | :applications | :included_applications | :registered | :maxT | :maxP | :modules | :vsn | :id | :description
key()
Specs
key() :: atom()
restart_type()
Specs
restart_type() :: :permanent | :transient | :temporary
start_type()
Specs
start_type() :: :normal | {:takeover, node()} | {:failover, node()} state()
Specs
state() :: term()
value()
Specs
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.
Если 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(app, key, opts \\ [])
Характеристики
delete_env(app(), key(), timeout: timeout(), persistent: boolean()) :: :ok
Удаляет key из заданной app среды.
Принимает те же опции, что и put_env/4. Возвращает :ok.
ensure_all_started(app, type \\ :temporary)
Характеристики
ensure_all_started(app(), restart_type()) ::
{:ok, [app()]} | {:error, {app(), term()}} Обеспечивает запуск данного app и его приложений.
То же, что и start/2, но также запускает приложения, перечисленные в :applications файле .app, если они не были ранее запущены.
ensure_started(app, type \\ :temporary)
Характеристики
ensure_started(app(), restart_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()
Возвращает значение для 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.
Примеры
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_all_env(config, opts \\ [])
Характеристики
put_all_env([{app(), [{key(), value()}]}],
timeout: timeout(),
persistent: boolean()
) :: :ok Одновременно устанавливает среду для нескольких приложений.
Указанная конфигурация не должна:
- содержать одно и то же приложение более одного раза
- содержать один и тот же ключ внутри одного и того же приложения более одного раза
Если эти условия не выполнены, поведение неопределенно (в Erlang/OTP 21 и ранее) или вызовет исключение (в Erlang/OTP 22 и более поздних версиях).
Принимает те же опции, что и put_env/4. Возвращает :ok.
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()) :: [{application_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(), application_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.9.4/Application.html