Наблюдатель поведение
Модуль поведения для реализации наблюдателей.
Наблюдатель — это процесс, который контролирует другие процессы, которые мы называем дочерними процессами. Наблюдатели используются для построения иерархической структуры процессов, называемой деревом наблюдения. Деревья наблюдения обеспечивают отказоустойчивость и инкапсулируют запуск и останов наших приложений.
Наблюдатель можно запустить напрямую со списком дочерних элементов с помощью start_link/2, или можно определить наблюдатель на основе модуля, реализующий необходимые обратные вызовы. Разделы ниже используют start_link/2 для запуска наблюдателей в большинстве примеров, но также включает раздел, посвящённый наблюдателям на основе модулей.
Примеры
Для запуска наблюдателя необходимо сначала определить дочерний процесс, который будет контролироваться. В качестве примера мы определим GenServer, который представляет собой стек:
defmodule Stack do
use GenServer
def start_link(state) do
GenServer.start_link(__MODULE__, state, name: __MODULE__)
end
## Callbacks
@impl true
def init(stack) do
{:ok, stack}
end
@impl true
def handle_call(:pop, _from, [h | t]) do
{:reply, h, t}
end
@impl true
def handle_cast({:push, h}, t) do
{:noreply, [h | t]}
end
endСтек — это небольшой оболочка вокруг списков. Он позволяет поместить элемент в верхнюю часть стека, добавив его в начало списка, и получить верхний элемент стека с помощью сопоставления с образцом.
Теперь мы можем запустить наблюдатель, который запустит и будет контролировать наш процесс стека. Первый шаг — определить список спецификаций дочерних процессов, которые управляют поведением каждого дочернего процесса. Каждая спецификация дочернего процесса — это карта, как показано ниже:
children = [
# The Stack is a child started via Stack.start_link([:hello])
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}
]
# Now we start the supervisor with the children and a strategy
{:ok, pid} = Supervisor.start_link(children, 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. Кроме того, каждый наблюдатель может иметь много рабочих процессов и других наблюдателей в качестве дочерних элементов, каждый со своей конфигурацией, значениями завершения и стратегиями перезапуска.
Остальная часть этого документа будет посвящена тому, как запускаются дочерние процессы, как они могут быть определены, различным стратегиям наблюдения и многому другому.
Запуск и останов
При запуске наблюдателя он проходит по всем спецификациям дочерних процессов и запускает каждый дочерний процесс в том порядке, в котором они определены. Это делается путем вызова функции, определённой под ключом :start в спецификации дочернего процесса, и по умолчанию это start_link/1.
Затем вызывается start_link/1 (или пользовательский) для каждого дочернего процесса. Функция start_link/1 должна возвращать {:ok, pid}, где pid — идентификатор процесса нового процесса, связанного с наблюдателем. Дочерний процесс обычно начинает свою работу, выполняя обратный вызов init/1. Как правило, обратный вызов init — это место, где мы инициализируем и настраиваем дочерний процесс.
Процесс завершения происходит в обратном порядке.
При завершении работы наблюдателя он завершает все дочерние процессы в обратном порядке, в котором они перечислены. Завершение происходит путём отправки сигнала завершения с помощью Process.exit(child_pid, :shutdown) дочернему процессу и ожидания определенного интервала времени, пока дочерний процесс не завершится. Этот интервал по умолчанию составляет 5000 миллисекунд. Если дочерний процесс не завершится в этот интервал, наблюдатель резко завершает его с причиной :kill. Время завершения можно настроить в спецификации дочернего процесса, что подробно описано в следующем разделе.
Если дочерний процесс не обрабатывает сигналы выхода, он завершится сразу после получения первого сигнала выхода. Если дочерний процесс обрабатывает сигналы выхода, тогда вызывается обратный вызов terminate, и дочерний процесс должен завершиться в разумный промежуток времени, прежде чем быть резко прерван наблюдателем.
Другими словами, если важно, чтобы процесс очистил за собой при завершении приложения или дерева наблюдения, то этот процесс должен обрабатывать сигналы выхода, и его спецификация дочернего процесса должна содержать правильное значение :shutdown, обеспечивая его завершение в разумный срок.
Теперь, когда мы понимаем процесс запуска и завершения, давайте подробнее рассмотрим все параметры, предоставляемые в спецификации дочернего процесса.
Спецификация дочернего процесса
Спецификация дочернего процесса описывает, как наблюдатель запускает, завершает и перезапускает дочерние процессы.
Спецификация дочернего процесса содержит 5 ключей. Первые два обязательны, а остальные — необязательны:
:id— значение, используемое для идентификации спецификации дочернего процесса внутри наблюдателя; по умолчанию — заданный модуль. В случае конфликтующих:id, наблюдатель откажется от инициализации и потребует явных идентификаторов. Этот ключ обязателен.:start— кортеж с модулем-функцией-аргументами, которые будут вызваны для запуска дочернего процесса. Этот ключ обязателен.:restart— атом, определяющий, когда завершенный дочерний процесс должен быть перезапущен (см. раздел «Значения перезапуска» ниже). Этот ключ необязателен и по умолчанию равен:permanent.:shutdown— атом, определяющий, как должен быть завершен дочерний процесс (см. раздел «Значения завершения» ниже). Этот ключ необязателен и по умолчанию равен5000, если тип —:worker, или:infinity, если тип —:supervisor.:type— если дочерний процесс является:workerили:supervisor. Этот ключ необязателен и по умолчанию равен:worker.
Есть шестой ключ, :modules, который редко меняется и устанавливается автоматически на основе значения в :start.
Давайте разберемся, что контролируют параметры :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}.
Для более полного понимания причин выхода и их влияния см. раздел «Причины выхода и перезапуск».
child_spec/1
При запуске наблюдателя мы передаём список спецификаций дочерних процессов. Эти спецификации — это карты, которые сообщают наблюдателю, как запускать, останавливать и перезапускать каждого из его дочерних процессов:
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}В приведённой выше карте определён наблюдатель с :id Stack, который запускается путём вызова Stack.start_link([:hello]).
Однако указание спецификации дочернего процесса для каждого дочернего элемента в виде карты может быть довольно подвержено ошибкам, так как мы можем изменить реализацию стека и забыть обновить его спецификацию. Вот почему Elixir позволяет вам передавать кортеж с именем модуля и аргументом start_link вместо спецификации:
children = [
{Stack, [:hello]}
]Наблюдатель затем вызовет Stack.child_spec([:hello]) для получения спецификации дочернего элемента. Теперь модуль Stack отвечает за построение собственной спецификации. По умолчанию use GenServer определяет функцию Stack.child_spec/1, которая возвращает ту же спецификацию дочернего элемента, что и раньше:
%{
id: Stack,
start: {Stack, :start_link, [[:hello]]}
}Также можно просто передать модуль Stack как дочерний элемент:
children = [ Stack ]
Когда указано только имя модуля, это эквивалентно {Stack, []}. В этом случае мы получим спецификацию дочернего элемента, похожую на эту:
%{
id: Stack,
start: {Stack, :start_link, [[]]}
}Заменив спецификацию карты на {Stack, [:hello]} или Stack, мы сохраняем спецификацию дочернего элемента, инкапсулированную в модуле Stack, используя реализацию по умолчанию, определённую в use GenServer. Теперь мы можем поделиться нашим рабочим процессом Stack с другими разработчиками, и они могут добавить его непосредственно в своё дерево наблюдения, не беспокоясь о низкоуровневых деталях рабочего процесса.
Если вам нужно получить доступ к работе или изменить работу рабочего процесса или наблюдателя, вы можете использовать функцию Supervisor.child_spec/2. Например, чтобы запустить стек с другим :id и значением :shutdown в 10 секунд (10 000 миллисекунд):
children = [
Supervisor.child_spec({Stack, [:hello]}, id: MyStack, shutdown: 10_000)
]Вызов Supervisor.child_spec/2 выше вернёт следующую спецификацию:
%{
id: MyStack,
start: {Stack, :start_link, [[:hello]]},
shutdown: 10_000
}Вы также можете настроить спецификацию дочернего элемента в самом модуле Stack, чтобы использовать другое значение :id или :shutdown путём передачи опций в use GenServer:
defmodule Stack do use GenServer, id: MyStack, shutdown: 10_000
Приведённые выше опции настроят функцию Stack.child_spec/1, определённую в use GenServer. Она принимает те же опции, что и функция Supervisor.child_spec/2.
Вы также можете полностью переопределить функцию child_spec/1 в модуле Stack и вернуть собственное описание дочернего элемента. Обратите внимание, что нет гарантии, что функция child_spec/1 будет вызвана процессом Supervisor, поскольку другие процессы могут вызвать её для получения описания дочернего элемента до достижения надзирателя.
Причины завершения и перезапуски
Надзиратель перезапускает дочерний процесс в зависимости от его конфигурации :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
@impl true
def init(_arg) do
children = [
{Stack, [:hello]}
]
Supervisor.init(children, 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) или изнутри обратного вызова init/1:
Supervisor.init([
{Stack, [:hello]}
], strategy: :one_for_one) Хотя мы упомянули, что надзиратель автоматически расширяет {Stack, [:hello]} до спецификации дочернего элемента, вызывая Stack.child_spec([:hello]), мы не формально определили все аргументы, принимаемые функциями start_link/2 и init/2. Исправим это сейчас.
Первый аргумент, переданный функции start_link/2, — это список дочерних элементов, которые могут быть:
- словарь, представляющий саму спецификацию дочернего элемента — как описано в разделе «Спецификация дочернего элемента»
- кортеж с модулем в качестве первого элемента и аргументом запуска во втором — например,
{Stack, [:hello]}. В этом случае вызывается функцияStack.child_spec([:hello])для получения спецификации дочернего элемента - модуль — например,
Stack. В этом случае вызывается функцияStack.child_spec([])для получения спецификации дочернего элемента
Второй аргумент — это список ключевых слов опций:
-
:strategy— опция стратегии перезапуска. Она может быть:one_for_one,:rest_for_oneили:one_for_all. Смотрите раздел «Стратегии». -
:max_restarts— максимальное количество перезапусков, разрешенных за заданный промежуток времени. По умолчанию3. -
:max_seconds— временной интервал, в течение которого применяется:max_restarts. По умолчанию5.
Опция :strategy обязательна и по умолчанию допускает максимальное количество перезапусков — 3 — в течение 5 секунд.
Стратегии
Надзиратели поддерживают различные стратегии надзора (через опцию :strategy, как показано выше):
-
:one_for_one— если дочерний процесс завершается, перезапускается только этот процесс. -
:one_for_all— если дочерний процесс завершается, все остальные дочерние процессы завершаются, а затем все дочерние процессы (включая завершившийся) перезапускаются. -
:rest_for_one— если дочерний процесс завершается, «остальные» дочерние процессы, то есть дочерние процессы после завершившегося в порядке запуска, завершаются. Затем завершившийся дочерний процесс и остальные дочерние процессы перезапускаются.
В вышеперечисленном случае завершение процесса относится к неудачному завершению, которое определяется опцией :restart.
Также существует устаревшая стратегия :simple_one_for_one, которая была заменена на DynamicSupervisor. Надзиратель :simple_one_for_one был похож на :one_for_one, но лучше подходит для динамического присоединения дочерних элементов. Многие функции в этом модуле вели себя немного иначе при использовании этой стратегии. Смотрите модуль DynamicSupervisor для получения дополнительной информации и стратегий миграции.
Регистрация имени
Надзиратель подчиняется тем же правилам регистрации имени, что и GenServer. Дополнительные сведения об этих правилах см. в документации для GenServer.
Сводка
Типы
- child()
- child_spec()
-
Спецификация надзирателя
- init_option()
-
Опции, переданные функциям
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)
-
Добавляет спецификацию дочернего элемента к
supervisorи запускает этот дочерний элемент - start_link(children, options)
-
Запускает надзирателя с заданными дочерними элементами
- start_link(module, arg, options \\ [])
-
Запускает модульно-базированный процесс надзирателя с заданным
moduleиarg - stop(supervisor, reason \\ :normal, timeout \\ :infinity)
-
Синхронно останавливает данный надзиратель с заданной
reason - terminate_child(supervisor, child_id)
-
Завершает указанный дочерний элемент, идентифицированный по идентификатору
- which_children(supervisor)
-
Возвращает список с информацией обо всех дочерних элементах данного надзирателя
Обратные вызовы
- init(args)
-
Обратный вызов, вызываемый при запуске надзирателя и при обновлении кода в режиме hot code upgrades
Типы
child()
child() :: pid() | :undefined
child_spec()
child_spec() :: %{
:id => term(),
:start => {module(), atom(), [term()]},
optional(:restart) => :permanent | :transient | :temporary,
optional(:shutdown) => :brutal_kill | non_neg_integer() | :infinity,
optional(:type) => :worker | :supervisor,
optional(:modules) => [module()] | :dynamic
} Спецификация контролируемого процесса
init_option()
init_option() ::
{: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()} | init_option() Значения параметров, используемые функциями start*
options()
options() :: [option(), ...]
Параметры, используемые функциями start*
strategy()
strategy() :: :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]}} 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 не найден, или если текущий процесс работает или перезапускается.
init(children, options)
init([:supervisor.child_spec() | {module(), term()} | module()], [init_option()]) ::
{: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. -
: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}.
start_child(supervisor, child_spec)
start_child(
supervisor(),
:supervisor.child_spec() | {module(), term()} | module() | [term()]
) :: on_start_child() Добавляет спецификацию контролируемого процесса к supervisor и запускает этот дочерний процесс.
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(module(), term()) :: on_start()
start_link(
[:supervisor.child_spec() | {module(), term()} | module()],
options()
) :: on_start() Запускает контролирующий процесс с заданными дочерними процессами.
children — список модулей, кортежей из двух элементов (модуль и аргументы) или карта со спецификацией контролируемого процесса. Стратегия должна быть указана через параметр :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, child_id)
terminate_child(supervisor(), term()) :: :ok | {:error, error}
when error: :not_found | :simple_one_for_one Завершает указанного ребёнка, идентифицированного по id.
Процесс завершается, если он существует. Спецификация ребёнка сохраняется, если ребёнок не временный.
Процесс не временного ребёнка может быть позже перезапущен менеджером. Процесс ребёнка также можно перезапустить явно, вызвав restart_child/2. Используйте delete_child/2 для удаления спецификации ребёнка.
При успешном выполнении функция возвращает :ok. Если для данного идентификатора ребёнка нет спецификации, функция возвращает {:error, :not_found}.
which_children(supervisor)
which_children(supervisor()) :: [
{term() | :undefined, child() | :restarting, :worker | :supervisor,
:supervisor.modules()}
] Возвращает список с информацией обо всех детях данного менеджера.
Обратите внимание, что вызов этой функции при управлении большим количеством детей в условиях низкой памяти может вызвать исключение недостатка памяти.
Функция возвращает список кортежей {id, child, type, modules}, где:
-
id- как определено в спецификации ребёнка -
child- PID соответствующего процесса ребёнка,:restartingесли процесс собирается перезапуститься, или:undefinedесли такого процесса нет -
type-:workerили:supervisor, как указано в спецификации ребёнка -
modules- как указано в спецификации ребёнка
Обработчики
init(args)
init(args :: term()) ::
{:ok, {:supervisor.sup_flags(), [:supervisor.child_spec()]}} | :ignore Обработчик, вызываемый для запуска менеджера и во время обновлений горячей загрузки кода.
Разработчики обычно вызывают Supervisor.init/2 в конце своего обработчика init, чтобы вернуть соответствующие флаги управления.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.6.6/Supervisor.html