Fork me on GitHub

OTP Супервизоры

Супервизоры это специальные процессы с единственной целью: мониторинг других процессов. Супервизоры позволяют нам создавать отказоустойчивые приложения при помощи автоматического перезапуска дочерних процессов в случае их отказа.

Содержание

Настройка

Магия супервизоров заключена в функции Supervisor.start_link/2. Помимо запуска нашего супервизора и его потомков, она позволяет нам определять стратегию, которую будет использовать наш супервизор для управления дочерними процессами.

Потомки определяются при помощи списка и функции worker/3, которую мы импортировали из Supervisor.Spec. Функция worker/3 принимает модуль, аргументы и набор опций. Под капотом worker/3 вызывает start_link/3 с нашими аргументами во время инициализации.

Давайте начнём, используя SimpleQueue из урока OTP Concurrency:

import Supervisor.Spec

children = [
  worker(SimpleQueue, [], [name: SimpleQueue])
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Если наш процесс упадёт или завершится, супервизор автоматически перезапустит его как ни в чём не бывало.

Стратегии перезапуска

На данный момент для Супервизора доступны четыре разные стратегии перезапуска:

Варианты перезапуска

Есть несколько вариантов реализации перезапуска после падения дочернего процесса:

Это необязательная опция, значение по умолчанию — :permanent.

Вложенность

Помимо воркеров, мы также можем наблюдать за другими супервизорами, создавая дерево надзора. Единственное отличие для нас — замена supervisor/3 на worker/3:

import Supervisor.Spec

children = [
  supervisor(ExampleApp.ConnectionSupervisor, [[name: ExampleApp.ConnectionSupervisor]]),
  worker(SimpleQueue, [[], [name: SimpleQueue]])
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Наблюдатель задач

Для задач есть специальный супервизор — Task.Supervisor. Он разработан для динамически создаваемых задач и под капотом использует :simple_one_for_one.

Настройка

Включение Task.Supervisor ничем не отличается от других супервизоров:

import Supervisor.Spec

children = [
  supervisor(Task.Supervisor, [[name: ExampleApp.TaskSupervisor, restart: :transient]]),
]

{:ok, pid} = Supervisor.start_link(children, strategy: :one_for_one)

Наблюдаемые задачи

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

{:ok, pid} = Task.Supervisor.start_child(ExampleApp.TaskSupervisor, fn -> background_work end)

Если наша задача преждевременно выйдет из строя, она будет перезапущена. Это может быть особенно полезно при работе со входящими соединениями и обработке фоновых задач.


Поделиться