Приложение поведение
Модуль для работы с приложениями и определения обратных вызовов приложений.
Приложения — это стандартный способ упаковки программного обеспечения в 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 после того, как дерево надзора было остановлено временем выполнения. Этот обратный вызов позволяет приложению выполнить любое завершающее очищение. Аргументом является состояние, возвращенное start/2, если оно было возвращено, или [] в противном случае. Возвращаемое значение stop/1 игнорируется.
Используя Application, модули получают реализацию по умолчанию для stop/1, которая игнорирует свой аргумент и возвращает :ok, но ее можно переопределить.
Модули обратного вызова приложений также могут реализовать необязательный обратный вызов prep_stop/1. При наличии prep_stop/1 вызывается перед завершением дерева надзора. Его аргументом является состояние, возвращенное start/2, если оно было возвращено, или [] в противном случае, а его возвращаемое значение передается в stop/1.
Жизненный цикл приложения
Загрузка приложений
Приложения загружаются, что означает, что среда выполнения находит и обрабатывает их файлы ресурсов:
Application.load(:ex_unit) #=> :ok
Если приложение включает другие приложения, они также загружаются. И процедура рекурсивно повторяется, если они, в свою очередь, включают приложения. Включенные приложения не связаны с приложениями в проектах Mix umbrella, это концепция Erlang/OTP, которая связана с согласованным запуском.
При загрузке приложения среда, указанная в его файле ресурсов, объединяется с любыми переопределениями из файлов конфигурации, переданных erl через опцию -config. Стоит отметить, что релизы передают sys.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. Он завершит работу всех приложений в обратном порядке их запуска.
С Erlang/OTP 19.1 сигнал SIGTERM от операционной системы автоматически преобразуется в System.stop/0. Erlang/OTP 20 дает пользователю больший контроль над сигналами ОС с помощью функции :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()
- key()
- restart_type()
- 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приложение
Обработчики
- prep_stop(state)
-
Вызывается перед остановкой приложения
- start(start_type, start_args)
-
Вызывается при запуске приложения
- start_phase(phase, start_type, phase_args)
-
Запускает приложение в синхронных фазах
- stop(state)
-
Вызывается после остановки приложения
Типы
app()
app() :: atom()
key()
key() :: atom()
restart_type()
restart_type() :: :permanent | :transient | :temporary
start_type()
start_type() :: :normal | {:takeover, node()} | {:failover, node()} 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.
Если 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 для описания параметров.
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()) :: value() | no_return()
Возвращает значение для key в среде app приложения.
Если параметр конфигурации не существует, генерирует ArgumentError исключение.
fetch_env(app, key)
fetch_env(app(), key()) :: {:ok, value()} | :error Возвращает значение для key в среде app приложения в кортеже.
Если параметр конфигурации не существует, функция возвращает :error.
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_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, переопределят ранее заданные.
Параметр сохранения можно установить в значение 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. Обратите внимание, что функция не очищает модули приложения.
Обратные вызовы
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.7.4/Application.html