Spec-Zone.ru › Elixir 1.17

Исходный код DynamicSupervisor поведение

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

Модуль Supervisor был разработан для работы в основном со статическими дочерними процессами, которые запускаются в заданном порядке при запуске супервизора. DynamicSupervisor запускается без дочерних процессов. Вместо этого, дочерние процессы запускаются по требованию с помощью start_child/2, и порядок запуска не определён. Это позволяет DynamicSupervisor поддерживать миллионы дочерних процессов, используя эффективные структуры данных, и выполнять определённые операции, такие как остановка, одновременно.

Примеры

Динамический супервизор запускается без дочерних процессов и часто с именем:

children = [
  {DynamicSupervisor, name: MyApp.DynamicSupervisor, strategy: :one_for_one}
]

Supervisor.start_link(children, strategy: :one_for_one)

Опции, указанные в спецификации дочернего процесса, описаны в start_link/1.

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

defmodule Counter do
  use GenServer

  def start_link(initial) do
    GenServer.start_link(__MODULE__, initial)
  end

  def inc(pid) do
    GenServer.call(pid, :inc)
  end

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

  def handle_call(:inc, _, count) do
    {:reply, count, count + 1}
  end
end

Мы можем использовать start_child/2 со спецификацией дочернего процесса для запуска сервера Counter:

{:ok, counter1} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 0})
Counter.inc(counter1)
#=> 0

{:ok, counter2} = DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 10})
Counter.inc(counter2)
#=> 10

DynamicSupervisor.count_children(MyApp.DynamicSupervisor)
#=> %{active: 2, specs: 2, supervisors: 0, workers: 2}

Масштабируемость и разделение

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

Вместо этого:

children = [
  {DynamicSupervisor, name: MyApp.DynamicSupervisor}
]

и:

DynamicSupervisor.start_child(MyApp.DynamicSupervisor, {Counter, 0})

Можно сделать так:

children = [
  {PartitionSupervisor,
   child_spec: DynamicSupervisor,
   name: MyApp.DynamicSupervisors}
]

а затем:

DynamicSupervisor.start_child(
  {:via, PartitionSupervisor, {MyApp.DynamicSupervisors, self()}},
  {Counter, 0}
)

В приведенном выше коде мы запускаем супервизор разделения, который по умолчанию запустит динамический супервизор для каждого ядра вашего компьютера. Затем вместо вызова DynamicSupervisor по имени вы вызываете его через супервизор разделения, используя self() в качестве ключа маршрутизации. Это означает, что каждый процесс будет назначен одному из существующих динамических супервизоров. Подробнее об этом читайте в документации по PartitionSupervisor.

Супервизоры на основе модулей

Аналогично Supervisor, динамические супервизоры также поддерживают супервизоры на основе модулей.

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

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
  end

  @impl true
  def init(_init_arg) do
    DynamicSupervisor.init(strategy: :one_for_one)
  end
end

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

use DynamicSupervisor

При use DynamicSupervisor, модуль DynamicSupervisor установит @behaviour DynamicSupervisor и определит функцию child_spec/1, чтобы ваш модуль можно было использовать в качестве дочернего в дереве супервизора.

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

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

Миграция из Supervisor's :simple_one_for_one

В случае использования устаревшей стратегии :simple_one_for_one из модуля Supervisor, вы можете перейти на DynamicSupervisor несколькими шагами.

Представьте данный «старый» код:

defmodule MySupervisor do
  use Supervisor

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

  def start_child(foo, bar, baz) do
    # This will start child by calling MyWorker.start_link(init_arg, foo, bar, baz)
    Supervisor.start_child(__MODULE__, [foo, bar, baz])
  end

  @impl true
  def init(init_arg) do
    children = [
      # Or the deprecated: worker(MyWorker, [init_arg])
      %{id: MyWorker, start: {MyWorker, :start_link, [init_arg]}}
    ]

    Supervisor.init(children, strategy: :simple_one_for_one)
  end
end

Его можно модернизировать до DynamicSupervisor следующим образом:

defmodule MySupervisor do
  use DynamicSupervisor

  def start_link(init_arg) do
    DynamicSupervisor.start_link(__MODULE__, init_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(init_arg) do
    DynamicSupervisor.init(
      strategy: :one_for_one,
      extra_arguments: [init_arg]
    )
  end
end

Разница в том, что DynamicSupervisor ожидает спецификацию дочернего процесса в момент вызова start_child/2, а не в обратном вызове init. Если при инициализации передаются какие-либо начальные аргументы, например [initial_arg], их можно передать в флаге :extra_arguments функции DynamicSupervisor.init/1.

Резюме

Типы

init_option()

Опции, передаваемые функциям start_link и init/1

on_start_child()

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

option()

Опции, передаваемые функциям start_link

strategy()

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

sup_flags()

Флаги супервизора, возвращаемые при init

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

init(init_arg)

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

Функции

child_spec(options)

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

count_children(supervisor)

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

init(options)

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

start_child(supervisor, child_spec)

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

start_link(options)

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

start_link(module, init_arg, opts \\ [])

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

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

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

terminate_child(supervisor, pid)

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

which_children(supervisor)

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

Типы

init_option()Исходный код

@type init_option() ::
  {:strategy, strategy()}
  | {:max_restarts, non_neg_integer()}
  | {:max_seconds, pos_integer()}
  | {:max_children, non_neg_integer() | :infinity}
  | {:extra_arguments, [term()]}

Опции, передаваемые функциям start_link и init/1

on_start_child()Исходный код

@type on_start_child() ::
  {:ok, pid()}
  | {:ok, pid(), info :: term()}
  | :ignore
  | {:error, {:already_started, pid()} | :max_children | term()}

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

option()Исходный код

@type option() :: GenServer.option()

Опции, передаваемые функциям start_link

strategy()Исходный код

@type strategy() :: :one_for_one

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

sup_flags()Исходный код

@type sup_flags() :: %{
  strategy: strategy(),
  intensity: non_neg_integer(),
  period: pos_integer(),
  max_children: non_neg_integer() | :infinity,
  extra_arguments: [term()]
}

Флаги супервизора, возвращаемые при init

END_OF_DOCUMENT_MARKER

Обработчики

init(init_arg)Source

@callback init(init_arg :: term()) :: {:ok, sup_flags()} | :ignore

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

Разработчики обычно вызывают DynamicSupervisor.init/1 в конце своего обработчика init, чтобы вернуть соответствующие флаги надзора.

Функции

child_spec(options)Source

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

Принимает те же параметры, что и start_link/1.

См. Supervisor для получения дополнительной информации о спецификациях подчиненных процессов.

count_children(supervisor)Source

@spec 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)Source

@spec init([init_option()]) :: {:ok, sup_flags()}

Получает набор options, которые инициализируют динамического надзирателя.

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

Он принимает те же options параметры, что и start_link/1 (кроме :name) и возвращает кортеж, содержащий параметры надзирателя.

Примеры

def init(_arg) do
  DynamicSupervisor.init(max_children: 1000)
end

start_child(supervisor, child_spec)Source

@spec start_child(
  Supervisor.supervisor(),
  Supervisor.child_spec()
  | {module(), term()}
  | module()
  | (old_erlang_child_spec :: :supervisor.child_spec())
) :: on_start_child()

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

child_spec должно быть допустимой спецификацией дочернего процесса, как подробно описано в разделе «Спецификация дочернего процесса» в документации для Supervisor. Процесс-потомок запускается в соответствии с указаниями в спецификации дочернего процесса. Обратите внимание, что хотя поле :id все еще требуется в спецификации, значение игнорируется и поэтому не должно быть уникальным.

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

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

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

Если у надзирателя уже есть N дочерних процессов таким образом, что N превышает количество :max_children процессов, установленных при инициализации надзирателя (см. init/1), тогда эта функция возвращает {:error, :max_children}.

start_link(options)Source

@spec start_link([option() | init_option()]) :: Supervisor.on_start()

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

Эта функция обычно не вызывается напрямую, вместо этого она вызывается при использовании DynamicSupervisor как дочернего процесса другого надзирателя:

children = [
  {DynamicSupervisor, name: MySupervisor}
]

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

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

Параметры

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

  • :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_link(module, init_arg, opts \\ [])Source

@spec start_link(module(), term(), [option()]) :: Supervisor.on_start()

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

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

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

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

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

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

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

@spec stop(Supervisor.supervisor(), reason :: term(), timeout()) :: :ok

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

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

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

terminate_child(supervisor, pid)Source

@spec terminate_child(Supervisor.supervisor(), pid()) :: :ok | {:error, :not_found}

Завершает заданный дочерний процесс, идентифицированный по pid.

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

which_children(supervisor)Source

@spec which_children(Supervisor.supervisor()) :: [
  {:undefined, pid() | :restarting, :worker | :supervisor,
   [module()] | :dynamic}
]

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

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

Функция возвращает список кортежей, содержащих:

  • id - всегда :undefined для динамических надзирателей

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

  • type - :worker или :supervisor в соответствии со спецификацией дочернего процесса

  • modules - как определено в спецификации дочернего процесса

Скачать версию ePub

Создано с использованием ExDoc (v0.34.1) для язык программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/DynamicSupervisor.html

Spec-Zone.ru

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