集群中的全局动态主管
Global Dynamic Supervisor in a cluster
我有一个独特的问题,我不需要在 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
问题似乎是主管在两个节点上都启动了。处理这个问题的最佳方法是什么?我真的需要主管是动态的,这样我才能有效地管理工作模块。可能是不同的注册表?
感谢您的帮助。
经过一番研究,我找到了解决方案:
我现在正在使用 https://github.com/bitwalker/swarm 来处理 pid 注册。这允许跨集群设置进程,并在其中一个节点出现故障时提供切换支持。
可以用一个简单的中心节点来监控其他节点,当然是一个supervisor。
这个中心节点只做启动,monitor,并使用数据库保存其他节点的状态和pid。
当一个节点加入和宕机时,您可以接收它的宕机消息并处理它(更新数据库)。
这种方式唯一的缺点就是只能有一个中心节点,但是这个节点做的事情简单,基本稳定,在我们的生产系统上运行了一年。
如果您想要与全局进程注册表一起使用的相当简单的解决方案,您可以更改动态主管 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
关于这个解决方案很多人会说你不应该这样做,因为在网络分裂的情况下,你最终会在集群中有两个或更多的全局进程。但是如果你实现一些节点 monitoring/tracker 你甚至可以处理这个问题,这样你就知道集群中有多少节点 "see" 了。
例如,如果集群大小为 5,那么您可以创建检查规则来检查您是否看到 3 个以上的节点,如果没有,那么您将安排下一次启动,例如 1 秒并尝试在全局范围内再次注册您的dynamic supervisor until check rule returns true (意味着你在多数群体中,你可以在该群体中提供一致性)。另一方面,如果你的节点是少数群体,并且已经持有全局动态主管,则将其关闭并在 1 秒后开始调度。
这是实现集群一致性的最简单方法,但您应该考虑一件事。这个 Dynamic Supervisor 将在单个节点上启动 worker,我相信你不想这样做,所以宁愿使用全局注册表和一些负载平衡算法来平衡应该在本地 supervisor 中启动的进程。
我有一个独特的问题,我不需要在 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
问题似乎是主管在两个节点上都启动了。处理这个问题的最佳方法是什么?我真的需要主管是动态的,这样我才能有效地管理工作模块。可能是不同的注册表?
感谢您的帮助。
经过一番研究,我找到了解决方案:
我现在正在使用 https://github.com/bitwalker/swarm 来处理 pid 注册。这允许跨集群设置进程,并在其中一个节点出现故障时提供切换支持。
可以用一个简单的中心节点来监控其他节点,当然是一个supervisor。
这个中心节点只做启动,monitor,并使用数据库保存其他节点的状态和pid。
当一个节点加入和宕机时,您可以接收它的宕机消息并处理它(更新数据库)。
这种方式唯一的缺点就是只能有一个中心节点,但是这个节点做的事情简单,基本稳定,在我们的生产系统上运行了一年。
如果您想要与全局进程注册表一起使用的相当简单的解决方案,您可以更改动态主管 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
关于这个解决方案很多人会说你不应该这样做,因为在网络分裂的情况下,你最终会在集群中有两个或更多的全局进程。但是如果你实现一些节点 monitoring/tracker 你甚至可以处理这个问题,这样你就知道集群中有多少节点 "see" 了。
例如,如果集群大小为 5,那么您可以创建检查规则来检查您是否看到 3 个以上的节点,如果没有,那么您将安排下一次启动,例如 1 秒并尝试在全局范围内再次注册您的dynamic supervisor until check rule returns true (意味着你在多数群体中,你可以在该群体中提供一致性)。另一方面,如果你的节点是少数群体,并且已经持有全局动态主管,则将其关闭并在 1 秒后开始调度。
这是实现集群一致性的最简单方法,但您应该考虑一件事。这个 Dynamic Supervisor 将在单个节点上启动 worker,我相信你不想这样做,所以宁愿使用全局注册表和一些负载平衡算法来平衡应该在本地 supervisor 中启动的进程。