DynamicSupervisor поведение
Наблюдатель, который динамически запускает дочерние процессы.
Модуль Supervisor был разработан для обработки в основном статических дочерних процессов, которые запускаются в заданном порядке при запуске наблюдателя. DynamicSupervisor запускается без дочерних процессов. Вместо этого дочерние процессы запускаются по требованию с помощью start_child/2. При завершении работы динамического наблюдателя все дочерние процессы завершаются одновременно, без гарантии порядка.
Примеры
Динамический наблюдатель запускается без дочерних процессов, часто под наблюдателем с стратегией наблюдения (единственная в настоящее время поддерживаемая стратегия — :one_for_one) и именем:
children = [
{DynamicSupervisor, strategy: :one_for_one, name: MyApp.DynamicSupervisor}
]
Supervisor.start_link(children, strategy: :one_for_one) Параметры, указанные в спецификации дочернего процесса, документированы в start_link/1.
После запуска динамического наблюдателя мы можем запускать дочерние процессы с помощью start_child/2, который получает спецификацию дочернего процесса:
{:ok, agent1} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Agent, fn -> %{} end})
Agent.update(agent1, &Map.put(&1, :key, "value"))
Agent.get(agent1, & &1)
#=> %{key: "value"}
{:ok, agent2} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Agent, fn -> %{} end})
Agent.get(agent2, & &1)
#=> %{}
DynamicSupervisor.count_children(MyApp.DynamicSupervisor)
#=> %{active: 2, specs: 2, supervisors: 0, workers: 2} Наблюдатели, основанные на модулях
Аналогично Supervisor, динамические наблюдатели также поддерживают наблюдателей, основанных на модулях.
defmodule MyApp.DynamicSupervisor do
# Automatically defines child_spec/1
use DynamicSupervisor
def start_link(arg) do
DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
@impl true
def init(_arg) do
DynamicSupervisor.init(strategy: :one_for_one)
end
end См. документацию Supervisor для обсуждения случаев, когда следует использовать наблюдатели, основанные на модулях.
Регистрация имен
Наблюдатель подчиняется тем же правилам регистрации имен, что и GenServer. Подробнее об этих правилах см. в документации для GenServer.
Миграция из Supervisor’s :simple_one_for_one
В случае использования устаревшей стратегии :simple_one_for_one из модуля Supervisor, вы можете перейти к DynamicSupervisor несколькими шагами.
Представьте предоставленный «старый» код:
defmodule MySupervisor do
use Supervisor
def start_link(arg) do
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# This will start child by calling MyWorker.start_link(initial_arg, foo, bar, baz)
Supervisor.start_child(__MODULE__, [foo, bar, baz])
end
@impl true
def init(initial_arg) do
children = [
# Or the deprecated: worker(MyWorker, [initial_arg])
%{id: MyWorker, start: {MyWorker, :start_link, [initial_arg]})
]
Supervisor.init(children, strategy: :simple_one_for_one)
end
end Он может быть обновлён до DynamicSupervisor так:
defmodule MySupervisor do
use DynamicSupervisor
def start_link(arg) do
DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
end
def start_child(foo, bar, baz) do
# If MyWorker is not using the new child specs, we need to pass a map:
# spec = %{id: MyWorker, start: {MyWorker, :start_link, [foo, bar, baz]}}
spec = {MyWorker, foo: foo, bar: bar, baz: baz}
DynamicSupervisor.start_child(__MODULE__, spec)
end
@impl true
def init(initial_arg) do
DynamicSupervisor.init(
strategy: :one_for_one,
extra_arguments: [initial_arg]
)
end
end Разница в том, что DynamicSupervisor ожидает спецификацию дочернего процесса в момент вызова start_child/2, а не в обратном вызове инициализации. Если при инициализации есть какие-либо начальные аргументы, например, [initial_arg], их можно передать в флаге :extra_arguments в DynamicSupervisor.init/1.
Резюме
Типы
- init_option()
-
Параметры, переданные
start_link/2иinit/1 - on_start_child()
-
Возвращаемые значения функций
start_child - option()
-
Значения параметров, используемые функциями
start* - options()
-
Параметры, используемые функциями
start* - strategy()
-
Поддерживаемые стратегии
- sup_flags()
-
Флаги наблюдателя, возвращаемые при инициализации
Функции
- child_spec(opts)
-
Возвращает спецификацию для запуска динамического наблюдателя под наблюдателем
- count_children(supervisor)
-
Возвращает карту, содержащую значения подсчёта для наблюдателя
- init(options)
-
Получает набор параметров, которые инициализируют динамический наблюдатель
- start_child(supervisor, child_spec)
-
Динамически добавляет спецификацию дочернего процесса к
supervisorи запускает этот дочерний процесс - start_link(options)
-
Запускает наблюдатель с заданными параметрами
- start_link(mod, args, opts \\ [])
-
Запускает процесс наблюдателя, основанный на модуле, с заданными
moduleиarg - stop(supervisor, reason \\ :normal, timeout \\ :infinity)
-
Синхронно останавливает заданный наблюдатель с заданным
reason - terminate_child(supervisor, pid)
-
Завершает указанный дочерний процесс, идентифицированный по
pid - which_children(supervisor)
-
Возвращает список с информацией обо всех дочерних процессах
Обратные вызовы
- init(args)
-
Обратный вызов, вызываемый для запуска наблюдателя и во время горячих обновлений кода
Типы
init_option()
init_option() ::
{:strategy, strategy()}
| {:max_restarts, non_neg_integer()}
| {:max_seconds, pos_integer()}
| {:max_children, non_neg_integer() | :infinity}
| {:extra_arguments, [term()]} Параметры, переданные start_link/2 и init/1
on_start_child()
on_start_child() ::
{:ok, pid()}
| {:ok, pid(), info :: term()}
| :ignore
| {:error, {:already_started, pid()} | :max_children | term()} Возвращаемые значения функций start_child
option()
option() :: {:name, Supervisor.name()} | init_option() Значения параметров, используемые функциями start*
options()
options() :: [option(), ...]
Параметры, используемые функциями start*
strategy()
strategy() :: :one_for_one
Поддерживаемые стратегии
sup_flags()
sup_flags() :: %{
strategy: strategy(),
intensity: non_neg_integer(),
period: pos_integer(),
max_children: non_neg_integer() | :infinity,
extra_arguments: [term()]
} Флаги наблюдателя, возвращаемые при инициализации
Функции
child_spec(opts) (since 1.6.1)
Возвращает спецификацию для запуска динамического наблюдателя под наблюдателем.
См. Supervisor.
count_children(supervisor) (since 1.6.0)
count_children(Supervisor.supervisor()) :: %{
specs: non_neg_integer(),
active: non_neg_integer(),
supervisors: non_neg_integer(),
workers: non_neg_integer()
} Возвращает карту, содержащую значения подсчёта для наблюдателя.
Карта содержит следующие ключи:
-
:specs— количество дочерних процессов -
:active— количество всех активно работающих дочерних процессов, управляемых этим наблюдателем -
:supervisors— количество всех наблюдателей, независимо от того, жив ли дочерний процесс -
:workers— количество всех рабочих процессов, независимо от того, жив ли дочерний процесс
init(options) (since 1.6.0)
init([init_option()]) :: {:ok, sup_flags()} Получает набор параметров, которые инициализируют динамический наблюдатель.
Обычно вызывается в конце обратного вызова init/1 наблюдателей, основанных на модулях. Подробнее см. раздел «Наблюдатели, основанные на модулях» в документации по модулю.
Параметры, полученные этой функцией, также поддерживаются start_link/2.
Эта функция возвращает кортеж, содержащий параметры наблюдателя.
Примеры
def init(_arg) do DynamicSupervisor.init(max_children: 1000, strategy: :one_for_one) end
Параметры
-
:strategy— параметр стратегии перезапуска. Единственное поддерживаемое значение —:one_for_one, что означает, что другие дочерние процессы не будут остановлены при остановке одного дочернего процесса. Подробнее о стратегиях см. в документации по модулюSupervisor. -
:max_restarts— максимальное количество перезапусков, разрешённых в течение заданного временного интервала. По умолчанию —3. -
:max_seconds— временной интервал, в котором действует:max_restarts. По умолчанию —5. -
:max_children— максимальное количество дочерних процессов, работающих под этим наблюдателем одновременно. При превышении значения:max_childrenфункцияstart_child/2возвращает{:error, :max_children}. По умолчанию —:infinity. -
:extra_arguments— аргументы, добавляемые перед аргументами, указанными в спецификации дочернего процесса, переданной вstart_child/2. По умолчанию — пустой список.
start_child(supervisor, child_spec) (since 1.6.0)
start_child(
Supervisor.supervisor(),
:supervisor.child_spec() | {module(), term()} | module()
) :: on_start_child() Динамически добавляет спецификацию дочернего процесса к supervisor и запускает этот дочерний процесс.
child_spec должна быть корректной спецификацией дочернего процесса, как подробно описано в разделе «child_spec/1» документации по Supervisor. Дочерний процесс будет запущен в соответствии с указанной спецификацией.
Если функция запуска дочернего процесса возвращает {:ok, child} или {:ok, child,
info}, то спецификация дочернего процесса и PID добавляются в наблюдатель, и эта функция возвращает то же значение.
Если функция запуска дочернего процесса возвращает :ignore, то дочерний процесс не добавляется в дерево наблюдения, и эта функция также возвращает :ignore.
Если функция запуска дочернего процесса возвращает кортеж с ошибкой или ошибочное значение или если произошла ошибка, то спецификация дочернего процесса отбрасывается, и эта функция возвращает {:error, error}, где error — термин, содержащий информацию об ошибке и спецификации дочернего процесса.
Если у руководителя уже есть N детей таким образом, что N превышает количество :max_children установленное при инициализации руководителя (см. init/1), то эта функция возвращает {:error, :max_children}.
start_link(options) (с версии 1.6.0)
start_link(options()) :: Supervisor.on_start()
Запускает руководителя с заданными параметрами.
Параметр :strategy является обязательным, и в настоящее время поддерживается значение :one_for_one. Остальные параметры можно найти в документации init/1.
Параметр :name также может использоваться для регистрации имени руководителя. Поддерживаемые значения описаны в разделе «Регистрация имени» в документации модуля GenServer.
Если руководитель успешно запущен, эта функция возвращает {:ok, pid}, где pid — PID руководителя. Если руководителю задано имя, и процесс с указанным именем уже существует, функция возвращает {:error, {:already_started, pid}}, где pid — PID этого процесса.
Обратите внимание, что руководитель, запущенный с помощью этой функции, связан с родительским процессом и завершается не только при сбоях, но и если родительский процесс завершается с причиной :normal.
start_link(mod, args, opts \\ []) (с версии 1.6.0)
start_link(module(), term(), GenServer.options()) :: Supervisor.on_start()
Запускает процесс руководителя, основанный на модуле, с заданными module и arg.
Для запуска руководителя вызывается обратный вызов init/1 в заданном module, с arg в качестве аргумента. Обратный вызов init/1 должен вернуть спецификацию руководителя, которую можно создать с помощью функции init/1.
Если обратный вызов init/1 возвращает :ignore, эта функция также возвращает :ignore и руководитель завершается с причиной :normal. Если произошел сбой или возвращено некорректное значение, эта функция возвращает {:error, term} где term — термин с информацией об ошибке, и руководитель завершается с причиной term.
Параметр :name также может быть задан для регистрации имени руководителя, поддерживаемые значения описаны в разделе «Регистрация имени» в документации модуля GenServer.
stop(supervisor, reason \\ :normal, timeout \\ :infinity) (с версии 1.7.0)
stop(Supervisor.supervisor(), reason :: term(), timeout()) :: :ok
Синхронно останавливает заданного руководителя с заданной reason.
Возвращает :ok если руководитель завершился с заданной причиной. Если он завершился по другой причине, вызов завершается.
Эта функция поддерживает семантику OTP в отношении сообщений об ошибках. Если причина отличается от :normal, :shutdown или {:shutdown, _}, регистрируется сообщение об ошибке.
terminate_child(supervisor, pid) (с версии 1.6.0)
terminate_child(Supervisor.supervisor(), pid()) :: :ok | {:error, :not_found} Завершает заданного дочернего процесса, идентифицируемого по pid.
При успешном завершении функция возвращает :ok. Если процесса с заданным PID нет, функция возвращает {:error, :not_found}.
which_children(supervisor) (с версии 1.6.0)
which_children(Supervisor.supervisor()) :: [
{:undefined, pid() | :restarting, :worker | :supervisor,
:supervisor.modules()}
] Возвращает список с информацией обо всех дочерних процессах.
Обратите внимание, что вызов этой функции при наблюдении за большим количеством дочерних процессов в условиях ограниченного объема оперативной памяти может привести к исключению из-за нехватки памяти.
Функция возвращает список кортежей, содержащих:
-
id— всегда:undefinedдля динамических руководителей -
child— pid соответствующего дочернего процесса или атом:restartingесли процесс собирается перезапуститься -
type—:workerили:supervisorкак определено в спецификации дочернего процесса -
modules— как определено в спецификации дочернего процесса
Обратные вызовы
init(args)
init(args :: term()) :: {:ok, sup_flags()} | :ignore Обратный вызов, вызываемый для запуска руководителя и во время горячих обновлений кода.
Разработчики обычно вызывают DynamicSupervisor.init/1 в конце своего обратного вызова init, чтобы вернуть соответствующие флаги управления.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.7.4/DynamicSupervisor.html