Spec-Zone.ru › Elixir 1.18

Источник 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/1 и init/1

on_start_child()

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

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/1 и 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

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

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

init(init_arg)Источник

@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([init_option() | GenServer.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. По умолчанию пустой список.

  • Любые стандартные параметры GenServer

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

@spec start_link(module(), term(), [GenServer.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 причиной.

Параметры

Эта функция принимает любые стандартные параметры GenServer. Параметры, специфичные для DynamicSupervisor, должны возвращаться из обратного вызова init/1.

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

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

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

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

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

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

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

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

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

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

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

Spec-Zone.ru

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