Как вызвать завершение дерева супервизора elixir из контролируемого рабочего процесса - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь завершить все дерево надзора из контролируемого рабочего процесса.Вот мое дерево наблюдения:

                   +--------------------------+
                   |                          |
          +--------+ Sup1: Dynamic Supervisor +---------+
          |        |                          |         |
          |        +-------------+------------+         |
          |                      |                      |
          |                      |                      |
          v                      v                      v

+------------------+   +------------------+  +------------------+
|                  |   |                  |  |                  |
| Job1: Supervisor |   | Job2: Supervisor |  | Job3: Supervisor |
|                  |   |                  |  |                  |
+------------------+   +-+-------- +---+--+  +------------------+
                         |             |
                         |             |
                         |             |
                         |             |
                         v             v

             +-------------------+  +--------------+
             |                   |  |              |
             | Progress Monitor: |  | Work: Worker |
             |       Worker      |  |              |
             |                   |  +--------------+
             +-------------------+

Жизненный цикл процесса:

  1. A Job запускается через: DynamicSupervisor.start_child(__MODULE__, spec)
  2. Каждое задание также является деревом контроля: 1 супервизор (стратегия перезапуска - one_for_one) -> 2 работника
  3. Progress Monitor работник знает, когда задание выполнено
  4. По выполненной работе, Progress Monitor работник делает попытку завершить все дерево контроля заданий, вызывая: DynamicSupervisor.terminate_child(__MODULE__, pid)
  5. Progress Monitor, как ожидается, выполнит шаги по очистке в terminate обратном вызове - он захватывает сигналы выхода

Проблемы и наблюдения:

  1. DynamicSupervisor.terminate_child - это блокирующий вызов, что означает, что он ожидает завершения всех дочерних процессов, включая вызывающийпроцесс - Progress Monitor
  2. Progress Monitor находится в тупике и не может завершиться.Родительский супервизор отправляет сигнал :kill, который не вызывает terminate обратный вызов

Быстрые обходные пути:

  1. Вызов DynamicSupervisor.terminate_childс Progress Monitor рабочий асинхронно:

    spawn(fn -> DynamicSupervisor.terminate_child(__MODULE__, pid) end)

  2. Определение стратегии отключения для Sup1: Dynamic Supervisor:

    shutdown: 5_000

    Он будет ждать не более 5 секунд для завершения дерева контроля работы, а затем отправит shutdown сигнал выхода.Это обеспечит вызов terminate обратного вызова для Progress Monitor процесса.

Не доволен ими обоими.

Вопросы:

  1. Как инициировать завершение дерева надзора из рабочего процесса и избежать тупиков?
  2. Если прекращение дерева надзора от работника не является наилучшей практикой, тогда какой путь рекомендуется?
  3. Какие-нибудь рекомендации, как изменить дерево наблюдения, чтобы облегчить изящное завершение?

1 Ответ

0 голосов
/ 14 ноября 2018

Просто вызовите его в асинхронной задаче Task.async(fn -> Process.exit(Sup1, :shutdown) end), он остановит Sup1, и с ним все дети отключатся

EDIT:

Если вам нужно более красивое решение, это зависит от того, что еще вам нужно. В большинстве случаев я создаю рабочий Bootstrapper, который будет выполнять инициализацию и некоторые другие вещи. Вы можете легко добавить другие функции.

Итак, учитывая вышеизложенное и, грубо говоря, я бы добавил в слой выше (AppSupervisor) еще один DynamicSupervisor, чтобы он мог запустить Bootstrapper и передать ему self() (или зарегистрировать его под локальным именем, чтобы избежать этой инъекции ). После этого при запуске рабочий Bootstrap запустит Sup1 (ваш динамический супервизор) и будет ожидать других сообщений, например, :terminate_sup1, который остановит Sup1 процесс. Позже, у некоторых из нижеперечисленных работников вы можете отключить Sup1, передав сообщение :terminate_sup1 загрузчику. Также есть дверь, которая позволяет вам снова запускать Sup1 при отправке другого сообщения работнику начальной загрузки.

Более того, если вам просто нужно отключить Sup1, просто перейдите к Задаче. Но если вам нужен контроль, поместите его в один рабочий процесс, который должен контролировать его, когда он включен или выключен.

...