Spec-Zone.ru › Elixir 1.7

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

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

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

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

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

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

Spec-Zone.ru

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