<commit_before>---
version: 1.0.1
title: OTP Супервизоры
---

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

{% include toc.html %}

## Настройка

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

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

Давайте начнём, используя SimpleQueue из урока [OTP Concurrency](../../advanced/otp-concurrency):

```elixir
import Supervisor.Spec

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

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

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

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

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

+ `:one_for_one` - Перезапуск только упавшего дочернего процесса.

+ `:one_for_all` - Перезапуск всех дочерних процессов из события падения.

+ `:rest_for_one` - Перезапуск упавшего процесса и всех процессов, запущенных после него.

+ `:simple_one_for_one` - Наилучшая для динамически прикрепляемых процессов стратегия. Спецификация супервизора обязана содержать описание только одного дочернего процесса, но он может быть запущен несколько раз. Предполагается, что эта стратегия используется, когда нужно динамически запускать и останавливать дочерние процессы.

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

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

+ `:permanent` - Дочерний процесс всегда перезапускается.

+ `:temporary` - Дочерний процесс никогда не перезапускается.

+ `:transient` - Дочерний процесс перезапускается только в случае аномального завершения.

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

### Вложенность

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

```elixir
import Supervisor.Spec

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

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

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

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

### Настройка

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

```elixir
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` для создания наблюдаемой задачи:

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

Если наша задача преждевременно выйдет из строя, она будет перезапущена.  Это может быть особенно полезно при работе со входящими соединениями и обработке фоновых задач.
<commit_msg>Update russian version of advanced/otp-supervisors (#2218)<commit_after>---
version: 1.1.1
title: OTP Супервизоры
---

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

{% include toc.html %}

## Настройка

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

Давайте начнём, используя `SimpleQueue` из урока [OTP Concurrency](../../advanced/otp-concurrency):

Создадим новый проект используя`mix new simple_queue --sup` для генерации проекта с деревом супервизора. Код для модуля `SimpleQueue` должен находиться в `lib/simple_queue.ex`, а код супервизора, который мы добавим, пойдет в `lib/simple_queue/application.ex`.

Дочерние процессы определяются с помощью списка двумя способами. Либо списком названий модулей:

```elixir
defmodule SimpleQueue.Application do
  use Application

  def start(_type, _args) do
    children = [
      SimpleQueue
    ]

    opts = [strategy: :one_for_one, name: SimpleQueue.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
```

Либо списком кортежей, если вы хотите добавить параметры конфигурации:

```elixir
defmodule SimpleQueue.Application do
  use Application

  def start(_type, _args) do
    children = [
      {SimpleQueue, [1, 2, 3]}
    ]

    opts = [strategy: :one_for_one, name: SimpleQueue.Supervisor]
    Supervisor.start_link(children, opts)
  end
end
```

Если мы выполним `iex -S mix`, то увидим, что `SimpleQueue` автоматически запустился:

```elixir
iex> SimpleQueue.queue
[1, 2, 3]
```

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

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

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

+ `:one_for_one` - Перезапуск только упавшего дочернего процесса.

+ `:one_for_all` - Перезапуск всех дочерних процессов из события падения.

+ `:rest_for_one` - Перезапуск упавшего процесса и всех процессов, запущенных после него.

## Спецификация дочерних процессов

После запуска супервизор должен знать, как запускать/останавливать/перезапускать свои дочерние процессы. Каждый модуль дочернего процесса должен иметь функцию `child_spec/1` для определения этих поведений. Макросы `use GenServer`, `use Supervisor`, и `use Agent` автоматически определяют этот метод для нас (`SimpleQueue` имеет `use Genserver`, поэтому нам не надо вносить правки в этот модуль), но если вам необходимо определить его вручную, `child_spec/1` должен вернуть ассоциативный массив параметров:

```elixir
def child_spec(opts) do
  %{
    id: SimpleQueue,
    start: {__MODULE__, :start_link, [opts]},
    shutdown: 5_000,
    restart: :permanent,
    type: :worker
  }
end
```

+ `id` - Обязательный ключ.
Используется супервизором для определения спецификации дочернего процесса.

+ `start` - Обязательный ключ.
Модуль/Функция/Агрументы для вызова, когда процесс запускается супервизором.

+ `shutdown` - Дополнительный ключ.
Определяет поведение при остановке.
Варианты:

  + `:brutal_kill` - Дочерний процесс останавливается незамедлительно

  + любое положительное число - время в миллисекундах, которое супервизор будет ждать перед убийством дочернего процесса.
Если процесс имеет тип `:worker`, время по умолчанию будет равно 5000.

  + `:infinity` - Супервизор будет бесконечно ждать завершения дочернего процесса.
По умолчанию для типа процесса `:supervisor`.
Не рекомендуется для типа `:worker`.

+ `restart` - Дополнительный ключ.
Существует несколько подходов для обработки падений дочерних процессов:

  + `:permanent` - Дочерний процесс всегда перезапускается.
По умолчанию для всех процессов

  + `:temporary` - Дочерний процесс никогда не перезапускается.

  + `:transient` - Дочерний процесс перезапускается только в случае аномального завершения.

+ `type` - Дополнительный ключ.
Процессы могут быть либо `:worker`, либо `:supervisor`.
По умолчанию `:worker`.

## Динамичный Супервизор

Супервизор обычно запускаются вместе со списком дочерних процессов во время запуска приложения. Однако, иногда дочерние процессы остаются неизвестны во время запуска нашего приложения (Например, мы можем иметь веб-приложение, которое запускает новые процессы для обработки подключений пользователей к нашему сайту).
В этих случаях нам нужен супервизор, где дочерние процессы могут быть запущены по требованию.
Решением является Динамичный Супервизор (DynamicSupervisor).

Поскольку мы не будем указывать дочерние процессы, нам нужно только определить параметры времени выполнения для супервизора.
Динамичный Супервизор поддерживает только стратегию `:one_for_one`:

```elixir
options = [
  name: SimpleQueue.Supervisor,
  strategy: :one_for_one
]

DynamicSupervisor.start_link(options)
```

Затем, чтобы запустить новый SimpleQueue динамически, мы используем `start_child/2`, которая берет на вход супервизор и модуль дочернего процесса с определенной спецификацией (повторюсь, т.к. `SimpleQueue` используют `use GenServer`, то спецификация дочернего процесса уже определена):

```elixir
{:ok, pid} = DynamicSupervisor.start_child(SimpleQueue.Supervisor, SimpleQueue)
```

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

Для задач есть специальный супервизор &mdash; `Task.Supervisor`.  Он разработан для динамически создаваемых задач и под капотом использует `DynamicSupervisor`.

### Настройка

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

```elixir
children = [
  {Task.Supervisor, name: ExampleApp.TaskSupervisor, restart: :transient}
]

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

Основное различие между `Supervisor` и `Task.Supervisor` в параметре перезапуска по умолчанию - `:temporary` (задачи никогда не будут перезапущены).

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

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

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

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

{'fraction_non_alphanumeric': 0.07852516193323368, 'fraction_numerical': 0.0036870951669157946, 'mean_word_length': 5.458172458172458, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 6, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5816452', 'n_tokens_mistral': 3902, 'n_tokens_neox': 4619, 'n_words': 1045}