Spec-Zone.ru › Elixir 1.15

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 callback. Если при инициализации переданы какие-либо начальные аргументы, такие как [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_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()Source

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

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

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

option()Source

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

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

strategy()Source

@type strategy() :: :one_for_one

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

sup_flags()Source

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

Флаги наблюдателя, возвращаемые при инициализации

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

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

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

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

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

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

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

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

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.15.4/DynamicSupervisor.html

Spec-Zone.ru

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