Spec-Zone.ru › Elixir 1.8

Приложение поведение

Модуль для работы с приложениями и определения обратных вызовов приложений.

Приложения — это стандартный способ упаковки программного обеспечения в 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

Остановка приложения без модуля обратного вызова определена, но, за исключением некоторых системных следов, на практике это операция бездействия.

Остановка приложения с модулем обратного вызова состоит из трёх этапов:

  1. Если он присутствует, вызывается необязательный обратный вызов prep_stop/1.
  2. Завершается ведущий надзиратель.
  3. Вызывается обязательный обратный вызов 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 для получения дополнительной информации.

END_OF_DOCUMENT_MARKER

Дополнительная информация

Для получения более подробной информации об приложениях, пожалуйста, обратитесь к документации модуля 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API