Spec-Zone.ru › Elixir 1.5

Наблюдатель поведение

Модуль поведения для реализации наблюдателей.

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

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

Примеры

Для определения наблюдателя нам сначала необходимо определить дочерний процесс, который будет контролироваться. В качестве примера мы определим GenServer, который представляет стек:

defmodule Stack do
  use GenServer

  def start_link(state) do
    GenServer.start_link(__MODULE__, state, name: __MODULE__)
  end

  ## Callbacks

  def init(stack) do
    {:ok, stack}
  end

  def handle_call(:pop, _from, [h | t]) do
    {:reply, h, t}
  end

  def handle_cast({:push, h}, t) do
    {:noreply, [h | t]}
  end
end

Стек — это небольшой оболочку над списками. Он позволяет поместить элемент в верхнюю часть стека, добавив его в начало списка, и получить верхний элемент стека с помощью сопоставления шаблонов.

Теперь мы можем запустить наблюдатель, который запустит и будет контролировать наш процесс стека следующим образом:

# Start the supervisor with the stack as a single child.
#
# The first element of the tuple is the module containing
# the child implementation, the second is the argument
# given to start_link, in this case a stack with `:hello`.
{:ok, pid} = Supervisor.start_link([
  {Stack, [:hello]}
], strategy: :one_for_one)

# After started, we can query the supervisor for information
Supervisor.count_children(pid)
#=> %{active: 1, specs: 1, supervisors: 0, workers: 1}

Обратите внимание, что при запуске GenServer мы регистрируем его с именем Stack, что позволяет напрямую обращаться к нему и получать содержимое стека:

GenServer.call(Stack, :pop)
#=> :hello

GenServer.cast(Stack, {:push, :world})
#=> :ok

GenServer.call(Stack, :pop)
#=> :world

Однако в нашем сервере стека есть ошибка. Если мы вызовем :pop и стек пуст, произойдет сбой, поскольку ни одна ветвь не соответствует:

GenServer.call(Stack, :pop)
** (exit) exited in: GenServer.call(Stack, :pop, 5000)

К счастью, поскольку сервер контролируется наблюдателем, наблюдатель автоматически запустит новый, с начальным стеком [:hello]:

GenServer.call(Stack, :pop)
#=> :hello

Наблюдатели поддерживают разные стратегии; в примере выше мы выбрали :one_for_one. Кроме того, каждый наблюдатель может иметь много рабочих процессов и наблюдателей в качестве дочерних процессов, каждый со своей специфической конфигурацией, значениями завершения и стратегиями перезапуска.

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

Процесс инициализации

В предыдущем разделе мы запустили наблюдатель с одним дочерним процессом:

Supervisor.start_link([
  {Stack, [:hello]}
], strategy: :one_for_one)

Первый аргумент, переданный в start_link, — это список дочерних процессов. В приведенном выше примере мы передали кортеж, где дочерний процесс реализован модулем Stack, и получает начальный аргумент [:hello] в Stack.start_link/1.

В общем случае запуск дочернего процесса происходит в три этапа:

  1. Сначала наблюдатель вызывает Stack.child_spec([:hello]). Эта функция должна вернуть спецификацию дочернего процесса, которая описывает, как контролируется процесс Stack. При вызове use GenServer, для вас автоматически определяется child_spec/1, но мы увидим, когда и как его можно настроить. Эта функция вызывается один раз при запуске наблюдателя (или в случае обновлений горячей загрузки кода).

  2. Спецификация дочернего процесса сообщает наблюдателю, какую функцию нужно вызвать для запуска дочернего процесса. По умолчанию это функция start_link/1, которая получает тот же аргумент и определена в том же модуле, что и функция child_spec/1. Эта функция вызывается каждый раз, когда необходим новый дочерний процесс. Например, когда мы завершили Stack в предыдущем сеансе, Stack.start_link([:hello]) была вызвана ещё раз для запуска нового стека.

  3. Наконец, Stack.start_link/1 запускает новый процесс, который выполняет Stack.init/1, отвечающий за настройку процесса, который будет реагировать на сообщения.

Подводя итог, когда вызывается Supervisor.start_link(children, opts), он проходит по списку дочерних процессов и получает их спецификации. Каждая спецификация описывает, как запускается каждый дочерний процесс, обычно через функцию start_link/1. Наблюдатель вызывает функцию start_link/1 при инициализации и всякий раз, когда дочерний процесс нужно перезапустить. Новый процесс, запущенный start_link/1, часто выполняет обратный вызов init как первый шаг. Обратный вызов init — это место, где мы инициализируем и настраиваем дочерний процесс.

Спецификация дочернего процесса

Спецификация дочернего процесса описывает, как наблюдателю следует запускать и контролировать дочерний процесс. Мы узнали, что при вызове use GenServer для нас автоматически определена спецификация Stack.child_spec/1. Давайте вызовем её и посмотрим, что она возвращает:

Stack.child_spec([:hello])
#=> %{
  id: Stack,
  start: {Stack, :start_link, [[:hello]]},
  restart: :permanent,
  shutdown: 5000,
  type: :worker
}

Спецификация дочернего процесса содержит 5 ключей. Первые два являются обязательными, а остальные — необязательными:

  • :id — значение, используемое для идентификации спецификации дочернего процесса внутри наблюдателя; по умолчанию соответствует заданному модулю. В случае конфликтующих :id, наблюдатель откажется от инициализации и потребует явных идентификаторов. Этот ключ является обязательным.

  • :start — кортеж с модулем-функцией-аргументами, которые нужно вызвать для запуска дочернего процесса. Этот ключ является обязательным.

  • :restart — атом, определяющий, когда завершенный дочерний процесс должен быть перезапущен (см. раздел «Значения перезапуска» ниже). Этот ключ является необязательным и по умолчанию равен :permanent.

  • :shutdown — атом, определяющий, как должен завершаться дочерний процесс (см. раздел «Значения завершения» ниже). Этот ключ является необязательным и по умолчанию равен 5000, если тип :worker, или :infinity, если тип :supervisor.

  • :type — если дочерний процесс является :worker или :supervisor. Этот ключ является необязательным и по умолчанию равен :worker.

Есть шестой ключ, называемый :modules, который редко изменяется и автоматически устанавливается на основе значения в :start.

В большинстве случаев модуль поведения, который вы реализуете, позаботится о настройке правильной child_spec/1 для вас. Например, use Supervisor определит child_spec/1, где :type установлено в :supervisor, и :shutdown равно :infinity. Тем не менее, если вам нужно настроить определенное поведение, вы можете сделать это, определив собственную функцию child_spec/1 или передав опции в use. Например, для указания GenServer с лимитом завершения 10 секунд (10 000 миллисекунд), можно сделать следующее:

use GenServer, shutdown: 10_000

Давайте разберёмся, что контролируют опции :shutdown и :restart.

Значения завершения (:shutdown)

В опции :shutdown поддерживаются следующие значения завершения:

  • :brutal_kill — дочерний процесс безусловно завершается с помощью Process.exit(child, :kill).

  • любое целое число ≥ 0 — количество времени в миллисекундах, которое наблюдатель будет ждать завершения дочерних процессов после отправки сигнала Process.exit(child, :shutdown). Если дочерний процесс не обрабатывает выходы, исходный сигнал :shutdown завершит дочерний процесс немедленно. Если дочерний процесс обрабатывает выходы, у него есть указанное количество времени в миллисекундах для завершения. Если он не завершится в течение заданного времени, дочерний процесс безусловно завершается наблюдателем с помощью Process.exit(child, :kill).

  • :infinity — работает как целое число, за исключением того, что наблюдатель будет ждать неопределённое время завершения дочернего процесса. Если дочерний процесс является наблюдателем, рекомендуется значение :infinity, чтобы дать наблюдателю и его дочерним процессам достаточно времени для завершения. Эта опция может использоваться с обычными рабочими процессами, но это не рекомендуется и требует особой осторожности. Если её не использовать осторожно, и дочерний процесс не завершится, это означает, что ваше приложение тоже никогда не завершится.

Значения перезапуска (:restart)

Опция :restart управляет тем, что наблюдатель должен считать успешным завершением или нет. Если завершение успешное, наблюдатель не перезапустит дочерний процесс. Если дочерний процесс завершился сбоем, наблюдатель запустит новый.

В опции :restart поддерживаются следующие значения перезапуска:

  • :permanent — дочерний процесс всегда перезапускается.

  • :temporary — дочерний процесс никогда не перезапускается, независимо от стратегии наблюдения.

  • :transient — дочерний процесс перезапускается только в том случае, если он завершается аномально, т. е. с причиной выхода, отличной от :normal, :shutdown или {:shutdown, term}.

Для более полного понимания причин выхода и их влияния см. следующий раздел «Причины выхода».

Причины выхода

Наблюдатель перезапускает дочерний процесс в зависимости от конфигурации :restart. Например, когда :restart установлено :transient, наблюдатель не перезапускает дочерний процесс, если он завершается с причиной :normal, :shutdown или {:shutdown, term}.

Итак, можно спросить: какую причину выхода мне следует выбрать при завершении? Есть три варианта:

  • :normal — в таких случаях выход не будет регистрироваться, перезапуска в транзитном режиме не будет, и связанные процессы не завершатся.

  • :shutdown или {:shutdown, term} — в таких случаях выход не будет регистрироваться, перезапуска в транзитном режиме не будет, и связанные процессы завершатся с той же причиной, если они не обрабатывают выходы.

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

Обратите внимание, что наблюдатель, достигший максимальной интенсивности перезапуска, завершится с причиной :shutdown. В этом случае наблюдатель будет перезапущен только в том случае, если его спецификация дочернего процесса была определена с опцией :restart установленной в :permanent (по умолчанию).

Модульные наблюдатели

В приведенном выше примере наблюдатель запускался путем передачи структуры наблюдения в start_link/2. Однако наблюдатели также могут быть созданы путём явного определения модуля наблюдения:

defmodule MyApp.Supervisor do
  # Automatically defines child_spec/1
  use Supervisor

  def start_link(arg) do
    Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
  end

  def init(_arg) do
    Supervisor.init([
      {Stack, [:hello]}
    ], strategy: :one_for_one)
  end
end

Различие между двумя подходами заключается в том, что наблюдатель на основе модуля предоставляет больший прямой контроль над инициализацией наблюдателя. Вместо вызова Supervisor.start_link/2 со списком дочерних процессов, которые автоматически инициализируются, мы определили наблюдатель вместе с обратным вызовом init/1 и вручную инициализировали дочерние процессы, вызвав Supervisor.init/2, передав те же аргументы, которые мы бы передали в start_link/2.

Модульный наблюдатель может быть предпочтительным, если:

  • Вам нужно выполнить какое-либо особое действие при инициализации наблюдателя, например, настроить таблицу ETS.

  • Вы хотите выполнить частичную горячую замену кода в дереве. Например, если вы добавляете или удаляете дочерние процессы, модульный наблюдатель напрямую добавит и удалит новые дочерние процессы, в то время как динамический наблюдатель требует перезапуска всего дерева для выполнения таких операций.

Примечание use Supervisor определяет функцию child_spec/1, позволяющую поместить определённый модуль в дерево контроля. Сгенерированный child_spec/1 может быть настроен следующими параметрами:

  • :id - идентификатор спецификации дочернего элемента, по умолчанию равен текущему модулю
  • :start - способ запуска дочернего процесса (по умолчанию вызов __MODULE__.start_link/1)
  • :restart - время, когда диспетчер должен быть перезапущен, по умолчанию :permanent

start_link/2, init/2 и стратегии

До сих пор мы запускали диспетчера, передавая единственного ребёнка в виде кортежа, а также стратегию под названием :one_for_one.

Supervisor.start_link([
  {Stack, [:hello]}
], strategy: :one_for_one)

Или:

Supervisor.init([
  {Stack, [:hello]}
], strategy: :one_for_one)

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

Первый аргумент, переданный функции start_link/2, представляет собой список дочерних элементов, которые могут быть:

  • модулем, например Stack. В этом случае это эквивалентно передаче {Stack, []} (что означает, что Stack.child_spec/1 вызывается со списком пустых ключевых слов)
  • кортежем с модулем в качестве первого элемента и аргументом запуска во втором - например, {Stack, [:hello]}. При использовании такого формата диспетчер получит спецификацию дочернего элемента из указанного модуля.
  • отображением, представляющим саму спецификацию дочернего элемента - например, карта спецификации дочернего элемента, описанная в предыдущем разделе.

Второй аргумент — список ключевых слов параметров:

  • :strategy - параметр стратегии перезапуска. Он может быть :one_for_one, :rest_for_one, :one_for_all, или :simple_one_for_one. См. раздел «Стратегии».

  • :max_restarts - максимальное количество перезапусков, разрешенных в течение заданного временного интервала. По умолчанию 3.

  • :max_seconds - временной интервал, в течение которого применяется :max_restarts. По умолчанию 5.

Параметр :strategy является обязательным и по умолчанию позволяет максимум 3 перезапуска в течение 5 секунд.

Стратегии

Диспетчеры поддерживают различные стратегии контроля (через параметр :strategy, как показано выше):

  • :one_for_one - если дочерний процесс завершается, перезапускается только этот процесс.

  • :one_for_all - если дочерний процесс завершается, все другие дочерние процессы завершаются, а затем все дочерние процессы (включая завершившийся) перезапускаются.

  • :rest_for_one - если дочерний процесс завершается, «остальные» дочерние процессы, т. е. дочерние процессы после завершившегося в порядке запуска, завершаются. Затем завершившийся дочерний процесс и остальные дочерние процессы перезапускаются.

  • :simple_one_for_one - аналогично :one_for_one, но лучше подходит при динамическом подключении дочерних элементов. Эта стратегия требует, чтобы спецификация диспетчера содержала только один дочерний элемент. Многие функции в этом модуле ведут себя немного по-другому, когда используется эта стратегия.

Простой один к одному

Диспетчер :simple_one_for_one полезен, когда вы хотите динамически запускать и останавливать контролируемые дочерние элементы. В качестве примера, давайте динамически запускать несколько агентов для сохранения состояния.

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

Supervisor.start_link [
  {Agent, fn -> 0 end}
]

так как в примере все агенты будут иметь одинаковое состояние. В таких случаях мы можем использовать функцию child_spec/2 для построения и переопределения полей в спецификации дочернего элемента:

# Override the :start field to have no args.
# The second argument has no effect thanks to it.
agent_spec =
  Supervisor.child_spec(Agent, start: {Agent, :start_link, []})

# We start a supervisor with a simple one for one strategy.
# The agent won't be started now but later on.
{:ok, sup_pid} =
  Supervisor.start_link([agent_spec], strategy: :simple_one_for_one)

# No child worker is active until start_child is called
Supervisor.count_children(sup_pid)
#=> %{active: 0, specs: 1, supervisors: 0, workers: 0}

Простая стратегия «один к одному» может определять только один дочерний элемент, который используется в качестве шаблона при вызове функции start_child/2.

После запуска диспетчера давайте динамически запускать агентов:

{:ok, agent1} = Supervisor.start_child(sup_pid, [fn -> 0 end])
Agent.update(agent1, & &1 + 1)
Agent.get(agent1, & &1) #=> 1

{:ok, agent2} = Supervisor.start_child(sup_pid, [fn -> %{} end])
Agent.get(agent2, & &1) #=> %{}

Supervisor.count_children(sup_pid)
#=> %{active: 2, specs: 1, supervisors: 0, workers: 2}

Регистрация имён

Диспетчер подчиняется тем же правилам регистрации имён, что и GenServer. Дополнительную информацию об этих правилах см. в документации по GenServer.

Обзор

Типы

child()
child_spec()

Спецификация диспетчера

flag()

Параметры, передаваемые функциям start_link/2 и init/2

name()

Имя диспетчера

on_start()

Возвращаемые значения функций start_link

on_start_child()

Возвращаемые значения функций start_child

option()

Значения параметров, используемые функциями start*

options()

Параметры, используемые функциями start*

strategy()

Поддерживаемые стратегии

supervisor()

Ссылка на диспетчер

Функции

child_spec(module_or_map, overrides)

Создаёт и переопределяет спецификацию дочернего элемента

count_children(supervisor)

Возвращает карту, содержащую значения счётчиков для данного диспетчера

delete_child(supervisor, child_id)

Удаляет спецификацию дочернего элемента, идентифицированного по child_id

init(children, options)

Принимает список дочерних элементов для инициализации и набор опций

restart_child(supervisor, child_id)

Перезапускает дочерний процесс, идентифицированный по child_id

start_child(supervisor, child_spec_or_args)

Динамически добавляет спецификацию дочернего элемента к supervisor и запускает этот дочерний элемент

start_link(children, options)

Запускает диспетчера с указанными дочерними элементами

start_link(module, arg, options \\ [])

Запускает процесс диспетчера с указанным module и arg

stop(supervisor, reason \\ :normal, timeout \\ :infinity)

Синхронно останавливает указанный диспетчер с указанным reason

terminate_child(supervisor, pid_or_child_id)

Завершает указанные дочерние элементы, идентифицированные по PID или идентификатору дочернего элемента

which_children(supervisor)

Возвращает список с информацией обо всех дочерних элементах данного диспетчера

Обработчики

init(args)

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

Типы

child()

child() :: pid() | :undefined

child_spec()

child_spec() :: :supervisor.child_spec()

Спецификация диспетчера

flag()

flag() ::
  {:strategy, strategy()} |
  {:max_restarts, non_neg_integer()} |
  {:max_seconds, pos_integer()}

Параметры, передаваемые функциям start_link/2 и init/2

name()

name() :: atom() | {:global, term()} | {:via, module(), term()}

Имя диспетчера

on_start()

on_start() ::
  {:ok, pid()} |
  :ignore |
  {:error, {:already_started, pid()} | {:shutdown, term()} | term()}

Возвращаемые значения функций start_link

on_start_child()

on_start_child() ::
  {:ok, child()} |
  {:ok, child(), info :: term()} |
  {:error, {:already_started, child()} | :already_present | term()}

Возвращаемые значения функций start_child

option()

option() :: {:name, name()} | flag()

Значения параметров, используемые функциями start*

options()

options() :: [option(), ...]

Параметры, используемые функциями start*

strategy()

strategy() ::
  :simple_one_for_one |
  :one_for_one |
  :one_for_all |
  :rest_for_one

Поддерживаемые стратегии

supervisor()

supervisor() :: pid() | name() | {atom(), node()}

Ссылка на диспетчер

Функции

child_spec(module_or_map, overrides)

child_spec(child_spec() | {module(), arg :: term()} | module(), keyword()) :: child_spec()

Создаёт и переопределяет спецификацию дочернего элемента.

Аналогично функциям start_link/2 и init/2, она ожидает module, {module, arg} или карту в качестве спецификации дочернего элемента. Если указан модуль, спецификация извлекается путём вызова module.child_spec(arg).

После извлечения спецификации дочернего элемента поля на config напрямую применяются к спецификации дочернего элемента. Если у config есть ключи, которые не соответствуют ни одному полю спецификации дочернего элемента, возникает ошибка.

См. раздел «Спецификация дочернего элемента» в документации по модулю для просмотра всех доступных ключей для переопределения.

Примеры

Эта функция часто используется для установки параметра :id при необходимости запуска одного и того же модуля несколько раз в дереве контроля:

Supervisor.child_spec({Agent, fn -> :ok end}, id: {Agent, 1})
#=> %{id: {Agent, 1},
      start: {Agent, :start_link, [fn -> :ok end]}}

Его также можно использовать, когда требуется изменить количество аргументов при запуске модуля по стратегии :simple_one_for_one, поскольку большинство аргументов могут быть заданы динамически:

Supervisor.child_spec(Agent, start: {Agent, :start_link, []})
#=> %{id: Agent,
      start: {Agent, :start_link, []}}

count_children(supervisor)

count_children(supervisor()) :: %{specs: non_neg_integer(), active: non_neg_integer(), supervisors: non_neg_integer(), workers: non_neg_integer()}

Возвращает карту, содержащую значения подсчета для данного надзирателя.

Карта содержит следующие ключи:

  • :specs - общий подсчёт потомков, живых или мёртвых

  • :active - подсчёт всех активно работающих дочерних процессов, управляемых этим надзирателем

  • :supervisors - подсчёт всех надзирателей, независимо от того, живы ли эти дочерние надзиратели

  • :workers - подсчёт всех рабочих процессов, независимо от того, живы ли эти дочерние рабочие процессы

delete_child(supervisor, child_id)

delete_child(supervisor(), term()) ::
  :ok |
  {:error, error} when error: :not_found | :simple_one_for_one | :running | :restarting

Удаляет спецификацию потомка, идентифицированную по child_id.

Соответствующий дочерний процесс не должен работать; используйте terminate_child/2, чтобы завершить его, если он работает.

Если успешно, эта функция возвращает :ok. Эта функция может вернуть ошибку с соответствующей кортежем ошибки, если child_id не найдена, или если текущий процесс работает или перезапускается.

Эта операция не поддерживается :simple_one_for_one надзирателями.

init(children, options)

init([:supervisor.child_spec() | {module(), term()} | module()], flag()) :: {:ok, tuple()}

Получает список потомков для инициализации и набор параметров.

Обычно вызывается в конце обратного вызова init/1 модульных надзирателей. См. разделы «Модульные надзиратели» и «start_link/2, init/2 и стратегии» в документации модуля для получения дополнительной информации.

Эта функция возвращает кортеж, содержащий флаги надзирателя и спецификации потомков.

Примеры

def init(_arg) do
  Supervisor.init([
    {Stack, [:hello]}
  ], strategy: :one_for_one)
end

Параметры

  • :strategy - параметр стратегии перезапуска. Он может быть :one_for_one, :rest_for_one, :one_for_all, или :simple_one_for_one. Подробнее о стратегиях можно узнать в документации модуля Supervisor.

  • :max_restarts - максимальное количество перезапусков, разрешенных в определённый промежуток времени. По умолчанию 3.

  • :max_seconds - промежуток времени, в котором применяется :max_restarts. По умолчанию 5.

Параметр :strategy обязателен и по умолчанию позволяет максимум 3 перезапуска в течение 5 секунд. Обратитесь к модулю Supervisor за подробным описанием доступных стратегий.

restart_child(supervisor, child_id)

restart_child(supervisor(), term()) ::
  {:ok, child()} |
  {:ok, child(), term()} |
  {:error, error} when error: :not_found | :simple_one_for_one | :running | :restarting | term()

Перезапускает дочерний процесс, идентифицированный по child_id.

Спецификация потомка должна существовать, и соответствующий дочерний процесс не должен работать.

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

Если функция запуска дочернего процесса возвращает {:ok, child} или {:ok, child, info}, PID добавляется к надзирателю, и эта функция возвращает то же самое значение.

Если функция запуска дочернего процесса возвращает :ignore, PID остаётся установленным в :undefined, а эта функция возвращает {:ok, :undefined}.

Эта функция может вернуть ошибку с соответствующей кортежем ошибки, если child_id не найдена, или если текущий процесс работает или перезапускается.

Если функция запуска дочернего процесса возвращает кортеж ошибки или ошибочное значение, или если она завершается неудачей, эта функция возвращает {:error, error}.

Эта операция не поддерживается :simple_one_for_one надзирателями.

start_child(supervisor, child_spec_or_args)

start_child(supervisor(), :supervisor.child_spec() | [term()]) :: on_start_child()

Динамически добавляет спецификацию потомка к supervisor и запускает этого потомка.

child_spec должен быть допустимой спецификацией потомка (если надзиратель не является :simple_one_for_one надзирателем, см. ниже). Дочерний процесс будет запущен в соответствии с определением в спецификации потомка.

В случае :simple_one_for_one, используется спецификация потомка, определённая в надзирателе, и вместо child_spec, ожидается произвольный список терминов. Дочерний процесс затем запускается путём добавления данного списка к существующим аргументам функции в спецификации потомка.

Если спецификация потомка с указанным идентификатором уже существует, child_spec отбрасывается, и эта функция возвращает ошибку с :already_started или :already_present, если соответствующий дочерний процесс работает или нет соответственно.

Если функция запуска дочернего процесса возвращает {:ok, child} или {:ok, child, info}, то спецификация потомка и PID добавляются к надзирателю, и эта функция возвращает то же самое значение.

Если функция запуска дочернего процесса возвращает :ignore, спецификация потомка добавляется к надзирателю, PID устанавливается в :undefined, и эта функция возвращает {:ok, :undefined}.

Если функция запуска дочернего процесса возвращает кортеж ошибки или ошибочное значение, или если она завершается неудачей, спецификация потомка отбрасывается, и эта функция возвращает {:error, error}, где error — термин, содержащий информацию об ошибке и спецификации потомка.

start_link(children, options)

start_link([:supervisor.child_spec() | {module(), term()} | module()], options()) :: on_start()
start_link(module(), term()) :: on_start()

Запускает надзирателя с заданными потомками.

Потомки — это список модулей, 2-элементные кортежи с модулем и аргументами или карта со спецификацией потомка. Стратегия должна быть указана через опцию :strategy. См. «start_link/2, init/2 и стратегии» для примеров и других опций.

Параметры также можно использовать для регистрации имени надзирателя. Поддерживаемые значения описаны в разделе «Регистрация имени» в документации модуля GenServer.

Если надзиратель и его дочерние процессы успешно запущены (если функция запуска каждого дочернего процесса возвращает {:ok, child}, {:ok, child, info}, или :ignore) эта функция возвращает {:ok, pid}, где pid — PID надзирателя. Если надзирателю задано имя и процесс с указанным именем уже существует, функция возвращает {:error, {:already_started, pid}}, где pid — PID этого процесса.

Если функция запуска любого из дочерних процессов завершается неудачей или возвращает кортеж ошибки или ошибочное значение, надзиратель сначала завершает с причиной :shutdown все уже запущенные дочерние процессы, а затем завершает себя и возвращает {:error, {:shutdown, reason}}.

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

start_link(module, arg, options \\ [])

start_link(module(), term(), GenServer.options()) :: on_start()

Запускает процесс надзирателя с заданным module и arg.

Для запуска надзирателя вызывается обратный вызов init/1 в заданном module, с arg в качестве аргумента. Обратный вызов init/1 должен вернуть спецификацию надзирателя, которая может быть создана с помощью функции init/2.

Если обратный вызов init/1 возвращает :ignore, эта функция также возвращает :ignore и надзиратель завершается с причиной :normal. Если произошла ошибка или возвращено неверное значение, эта функция возвращает {:error, term} где term — термин с информацией об ошибке, а надзиратель завершается с причиной term.

Параметр :name также может быть задан для регистрации имени надзирателя, поддерживаемые значения описаны в разделе «Регистрация имени» в документации модуля GenServer.

stop(supervisor, reason \\ :normal, timeout \\ :infinity)

stop(supervisor(), reason :: term(), timeout()) :: :ok

Синхронно останавливает заданного надзирателя с указанной reason.

Возвращает :ok если надзиратель завершился с данной причиной. Если он завершился с другой причиной, вызов завершается.

Эта функция поддерживает семантику OTP по поводу сообщения об ошибках. Если причина отличается от :normal, :shutdown или {:shutdown, _}, регистрируется сообщение об ошибке.

terminate_child(supervisor, pid_or_child_id)

terminate_child(supervisor(), pid() | term()) ::
  :ok |
  {:error, error} when error: :not_found | :simple_one_for_one

Завершает указанных потомков, идентифицированных по PID или идентификатору потомка.

Если надзиратель не является :simple_one_for_one, ожидается идентификатор потомка, и процесс, если таковой имеется, завершается; спецификация потомка сохраняется, если потомок не временный.

В случае :simple_one_for_one надзирателя ожидается PID. Если идентификатор спецификации потомка указан вместо pid, эта функция возвращает {:error, :simple_one_for_one}.

Невременной дочерний процесс может быть позже перезапущен надзирателем. Дочерний процесс также может быть явно перезапущен, вызвав restart_child/2. Используйте delete_child/2, чтобы удалить спецификацию потомка.

При успешном выполнении эта функция возвращает :ok. Если для данного идентификатора потомка нет спецификации потомка или нет процесса с данным PID, эта функция возвращает {:error, :not_found}.

which_children(supervisor)

which_children(supervisor()) :: [{term() | :undefined, child() | :restarting, :worker | :supervisor, :supervisor.modules()}]

Возвращает список с информацией обо всех потомках данного надзирателя.

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

Функция возвращает список кортежей {id, child, type, modules}, где:

  • id - как определено в спецификации потомка или :undefined в случае simple_one_for_one надзирателя

  • child - PID соответствующего дочернего процесса, :restarting если процесс собирается перезапускаться, или :undefined если такого процесса нет

  • type - :worker или :supervisor, как указано в спецификации потомка

  • modules - как указано в спецификации потомка

Обратные вызовы

init(args)

init(args :: term()) ::
  {:ok, {:supervisor.sup_flags(), [:supervisor.child_spec()]}} |
  :ignore

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

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.5.3/Supervisor.html

Spec-Zone.ru

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