我想在erlang集群中启动一个唯一的全局注册gen_server进程。如果进程停止或运行它的节点发生故障,则将在其他节点之一上启动该进程。
该流程是主管的一部分。问题是在第二个节点上启动管理程序失败,因为gen_server已经在运行并从第一个节点全局注册。
{ok, Pid}
而不是启动新的gen_server实例?
global:trans()
吗?
start_link() ->
global:trans({?MODULE, ?MODULE}, fun() ->
case gen_server:start_link({global, ?MODULE}, ?MODULE, [], []) of
{ok, Pid} ->
{ok, Pid};
{error, {already_started, Pid}} ->
link(Pid),
{ok, Pid};
Else -> Else
end
end).
答案 0 :(得分:5)
如果您返回{ok,Pid}某些您未链接到的内容,则会让依赖于返回值的主管感到困惑。如果您不打算让主管将其用作start_link函数,那么您可以使用它。
您的方法似乎应该有效,因为如果全局实例死亡,每个节点都会尝试启动新实例。您可能会发现需要在管理程序设置中增加MaxR
值,因为每次集群成员更改时都会收到进程消息。
我过去创建全局单例的一种方法是在所有节点上运行该进程,但其中一个(赢得全局注册竞赛的那个)是主节点。其他进程监视主服务器,当主服务器退出时,尝试成为主服务器。 (再次,如果他们没有赢得注册竞赛,那么他们会监控那个人的pid)。如果你这样做,你必须自己处理全局名称注册(即不要使用gen_server:start({global, ...
功能),因为你希望过程开始,无论它是否赢得注册,它只会在每个注册中表现不同情况下。
流程本身必须更复杂(它必须在主模式和非主模式下运行),但它可以快速稳定,并且不会在管理员启动尝试时产生大量日志垃圾邮件。
我的方法通常需要几轮修改来摆脱极端情况,但在我看来比编写OTP分布式应用程序更麻烦。此方法与分布式应用程序相比具有另一个优势,因为您无需静态配置群集中涉及的节点列表 - 任何节点都可以作为运行进程主副本的候选节点。你的方法具有相同的属性。
答案 1 :(得分:4)
如何将gen_server转换为应用程序并使用distributed applications?