Spec-Zone.ru › Elixir 1.16

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

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

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

Примеры

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

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

Supervisor.start_link(children, strategy: :one_for_one)

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

После запуска динамического supervisor мы можем использовать его для запуска дочерних процессов по требованию. В данном примере 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}
)

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

Supervisors на основе модулей

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

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 для обсуждения случаев, когда могут потребоваться supervisors на основе модулей. Аннотация @doc сразу перед use DynamicSupervisor будет прикреплена к сгенерированной функции child_spec/1.

use DynamicSupervisor

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

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

Supervisor подчиняется тем же правилам регистрации имен, что и 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, а не в обратном вызове инициализации. Если при инициализации переданы какие-либо начальные аргументы, например, [initial_arg], их можно передать в флаг :extra_arguments в DynamicSupervisor.init/1.

Краткое описание

Типы

init_option()

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

on_start_child()

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

option()

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

strategy()

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

sup_flags()

Флаги supervisor, возвращаемые при инициализации

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

init(init_arg)

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

Функции

child_spec(options)

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

count_children(supervisor)

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

init(options)

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

start_child(supervisor, child_spec)

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

start_link(options)

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

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

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

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

Синхронно останавливает заданный supervisor с указанным 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()]
}

Флаги supervisor, возвращаемые при инициализации

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

init(init_arg)Исходный код

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

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

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

Функции

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, то ни один дочерний процесс не добавляется в дерево надзора, и эта функция также возвращает %%%CODE_BLOCK_87%%.

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

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

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 этого процесса.

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

Параметры

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

  • :strategy - опция стратегии перезапуска. Единственное поддерживаемое значение — :one_for_one, что означает, что ни один другой дочерний процесс не завершается, если дочерний процесс завершается. Вы можете узнать больше о стратегиях в документации модуля Supervisor.

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

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

  • :max_children - максимальное количество дочерних процессов, которые могут выполняться одновременно под этим надзирателем. Когда :max_children превышено, start_child/2 возвращает %%%CODE_BLOCK_114%%. По умолчанию %%%CODE_BLOCK_115%%.

  • :extra_arguments - аргументы, которые добавляются в начало аргументов, указанных в спецификации дочернего процесса, переданной в start_child/2. По умолчанию пустой список.

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

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

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

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

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

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

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

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

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

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

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

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

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

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}
]

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

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

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

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

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

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

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

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

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

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

Spec-Zone.ru

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