Глобальный динамический супервизор в кластере
У меня есть уникальная проблема, с которой мне не нужно было обращаться в elxir.
Мне нужно использовать динамический супервизор для динамического запуска (n) числа детей в кластерной среде. Я использую libcluster для управления кластеризацией и использования глобального реестра процессов для поиска динамического диспетчера pid.. Вот что происходит:
global: Name conflict terminating {:packer_supervisor, #PID<31555.1430.0>}
Вот код для супервизора:
defmodule EcompackingCore.PackerSupervisor do
use DynamicSupervisor
require Logger
def start_link() do
DynamicSupervisor.start_link(__MODULE__, :ok, name: {:global, :packer_supervisor})
end
def init(:ok) do
Logger.info("Starting Packer Supervisor")
DynamicSupervisor.init(strategy: :one_for_one)
end
def add_packer(badge_id, packer_name) do
child_spec = {EcompackingCore.Packer, {badge_id, packer_name}}
DynamicSupervisor.start_child(:global.whereis_name(:packer_supervisor), child_spec)
end
def remove_packer(packer_pid) do
DynamicSupervisor.terminate_child(:global.whereis_name(:packer_supervisor), packer_pid)
end
def children do
DynamicSupervisor.which_children(:global.whereis_name(:packer_supervisor))
end
def count_children do
DynamicSupervisor.count_children(:global.whereis_name(:packer_supervisor))
end
end
Проблема заключается в том, что супервизор запускается на обоих узлах. Какой был бы лучший способ справиться с этим? Мне действительно нужен супервизор, чтобы быть динамичным, чтобы я мог эффективно управлять рабочими модулями. Возможно, другой реестр?
Спасибо за вашу помощь.
Ответы
Ответ 1
После небольшого исследования я нашел решение:
Теперь я использую https://github.com/bitwalker/swarm для обработки регистрации pid. Это позволяет настраивать процессы в кластере и предлагает поддержку от руки, если один из узлов опускается.
Ответ 2
Вы можете использовать простой центральный узел для контроля других узлов, конечно, одного супервизора.
Этот центральный узел только запускает, контролирует и использует базу данных для сохранения статуса других узлов и pid.
Когда узел соединяется и опускается, вы можете получить его сообщение вниз и обработать его (обновить базу данных).
Единственным недостатком этого метода является то, что у вас может быть только один центральный узел, но поскольку этот узел выполняет простые вещи, он почти стабилен, который работает на нашей производственной системе в течение года.
Ответ 3
Если вы хотите довольно простое решение, которое работает с глобальным реестром процессов, вы можете изменить динамический супервизор start_link
defmodule EcompackingCore.PackerSupervisor do
use DynamicSupervisor
require Logger
def start_link() do
case DynamicSupervisor.start_link(__MODULE__, :ok, name: {:global, :packer_supervisor}) do
{:ok, pid} ->
{:ok, pid}
{:error, {:already_started, pid}} ->
# you need this pid so on each node supervisor
# of this dynamic supervisor can monitor this same pid
# so each node tracks existence of your process
{:ok, pid}
any -> any
end
end
def init(:ok) do
Logger.info("Starting Packer Supervisor")
DynamicSupervisor.init(strategy: :one_for_one)
end
def add_packer(badge_id, packer_name) do
child_spec = {EcompackingCore.Packer, {badge_id, packer_name}}
DynamicSupervisor.start_child(:global.whereis_name(:packer_supervisor), child_spec)
end
def remove_packer(packer_pid) do
DynamicSupervisor.terminate_child(:global.whereis_name(:packer_supervisor), packer_pid)
end
def children do
DynamicSupervisor.which_children(:global.whereis_name(:packer_supervisor))
end
def count_children do
DynamicSupervisor.count_children(:global.whereis_name(:packer_supervisor))
end
end
Многие люди скажут об этом решении, что вы не должны этого делать, поскольку в случае разделения сети вы получаете два или более глобальных процесса в кластере. Но вы даже можете справиться с этим, если вы реализуете некоторый мониторинг/отслеживание узлов, чтобы вы знали, сколько узлов вы видите в кластере.
Например, если размер кластера iz 5, вы можете создать правило проверки, которое будет проверять, если вы увидите узлы 3+, если нет, то вы планируете следующий старт, скажем 1 секунду, и попробуйте снова зарегистрировать глобальный динамический супервизор до тех пор, пока не проверите правило возвращает true (это означает, что вы находитесь в мажоритарной группе, и вы можете обеспечить согласованность в этой группе). С другой стороны, если ваш узел является группой меньшинства и уже имеет глобальный динамический супервизор, тогда выключите его и начните расписание за 1 секунду.
Это самый простой способ добиться согласованности между кластерами, но есть одна вещь, которую вы должны учитывать. Этот динамический супервизор запустит рабочих на одном узле, который, я уверен, вы не хотите, скорее используйте глобальный реестр и некоторый алгоритм балансировки нагрузки, чтобы сбалансировать процессы, которые должны запускаться в локальных супервизорах.
- У Роя есть встроенные кольца и алгоритмы статического кворума, но он использует хеширование для распределения нагрузки между кластерами. Это хорошее решение, если у ваших работников есть ID, для которого вы можете рассчитать хэш.
- Syn - еще одна альтернатива.