Динамический надзор — реализация надзора, при котором поднадзорные акторы добавляются и удаляются во время работы программы, а не определяются в исходном коде. То же: dynamic supervision.
Для динамического надзора нам понадобится действующий надзиратель. Его, конечно, тоже можно запустить динамически, но, я полагаю, в подавляющем большинстве случаев в этом нет необходимости. Поэтому можно считать, что динамический надзор осуществляется только за работниками.
Пусть мы даже вообще не знаем изначально, какие поднадзорные акторы будут запущены, всё равно супервизора надо запустить как полагается, указав список спецификаций поднадзорных. В нашем случае этот список пустой:
init([]) ->
Actors = [
% #{id => da,
% start => {da, start_link, [da]},
% type => worker,
% restart => permanent,
% shutdown => brutal_kill}
],
{ok, {{one_for_one, 20, 1}, Actors}}.
Здесь я закомментировал строки спецификации одного поднадзорного работника с именем da, чтобы напомнить, как эта спецификация вообще выглядит. Последняя строка определяет в целом особенность работы данного надзирателя. В ней надо вернуть в т.ч. список спецификаций поднадзорных.
Для динамического надзора нам потребуется знать несколько функций:
Создавать и удалять поднадзорных акторов, конечно, можно из любого места нашей Системы, где это нам удобно. И если угодно, можно управлять работающими акторами вручную из оболочки.
Для начала нам стоит решить, каким образом будет определён список нужных (разрешённых) акторов на данный момент. Можно реализовать этот список, например, в виде consult-файла, который мы собираемся в дальнейшем править вручную:
[da, abc, test, some, hello].
Consult-файл читается и сразу превращается одной строкой в терм.
{ok,[L]} = file:consult("/path/to/spisok"),
% L = [da,abc,test,some,hello]
Может быть два случая: надо кого-то запустить, надо кого-то остановить. Поэтому нам предстоит решить две задачи:
Строго говоря, можно попробовать запустить уже запущенный актор, это не приведёт к ошибке, просто появится ответ, что актор уже запущен, нового не будет. Но нам, хочется, конечно, более красивого решения — не запускать уже запущенные акторы. Опять же, это была бы какая-никакая, но дополнительная нагрузка на Систему.
Раз зашла речь про множества, логично вспомнить про модули работающие с множествами, например sets. Можно было бы и самим сделать карту для проверки, запущен актор или нет, но с sets это чуть изящнее.
Pa = [X || {X,_,_,_} <- supervisor:which_children(Надзиратель)],
PaS = sets:from_list(Pa),
Pa — список атомов поднадзорных акторов. PaS — то же, но превращённое в множество. Нам осталось лишь пробежаться по списку разрешённых акторов L, проверить в каждом случае, есть ли он во множестве PaS и если нет — запустить актора. Это удобно сделать с помощью задания списка.
[supervisor:start_child(Надзиратель,
#{id => X,
start => {X, start_link, [X]},
type => worker,
restart => permanent,
shutdown => brutal_kill}
) || X <- L, not sets:is_element(X,PaS)],
Теперь все разрешённые акторы запущены. Осталось решить вторую задачу — остановить и удалить акторов, которых уже нет в списке L. Будем действовать аналогично. Нам надо перебрать список запущенных акторов Pa и свериться с множеством разрешённых акторов. Его ещё у нас нет.
LS = sets:from_list(L),
Нам надо совершить два действия (остановить и удалить). Для наглядности сделаем это двумя заданиями списков:
[supervisor:terminate_child(Надзиратель,X) || X <- Pa, not sets:is_element(X,LS)],
[supervisor:delete_child(Надзиратель,X) || X <- Pa, not sets:is_element(X,LS)],
После этого у данного надзирателя останутся только разрешённые поднадзорные акторы.
h(supervisor)
Copyright © 2025-2026 Алексей Карманов