Erlang:erlang集群中单例gen_server的最佳方式?

时间:2010-12-16 09:56:31

标签: erlang global gen-server

环境:

我想在erlang集群中启动一个唯一的全局注册gen_server进程。如果进程停止或运行它的节点发生故障,则将在其他节点之一上启动该进程。

该流程是主管的一部分。问题是在第二个节点上启动管理程序失败,因为gen_server已经在运行并从第一个节点全局注册。

问题(S):

  • 是否可以检查进程是否已在gen_server的start_link函数中全局注册,并且在这种情况下返回已经运行的进程的{ok, Pid}而不是启动新的gen_server实例?
  • 这是正确的,这样一个进程将成为多个主管的一部分,如果一个进程发生故障,所有其他节点上的所有主管都会尝试重新启动该进程。第一个主管将创建一个新的gen_server进程,其他主管将再次链接到该进程。
  • 我应该在gen_server的start_link函数中使用某种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).


2 个答案:

答案 0 :(得分:5)

如果您返回{ok,Pid}某些您未链接到的内容,则会让依赖于返回值的主管感到困惑。如果您不打算让主管将其用作start_link函数,那么您可以使用它。

您的方法似乎应该有效,因为如果全局实例死亡,每个节点都会尝试启动新实例。您可能会发现需要在管理程序设置中增加MaxR值,因为每次集群成员更改时都会收到进程消息。

我过去创建全局单例的一种方法是在所有节点上运行该进程,但其中一个(赢得全局注册竞赛的那个)是主节点。其他进程监视主服务器,当主服务器退出时,尝试成为主服务器。 (再次,如果他们没有赢得注册竞赛,那么他们会监控那个人的pid)。如果你这样做,你必须自己处理全局名称注册(即不要使用gen_server:start({global, ...功能),因为你希望过程开始,无论它是否赢得注册,它只会在每个注册中表现不同情况下。

流程本身必须更复杂(它必须在主模式和非主模式下运行),但它可以快速稳定,并且不会在管理员启动尝试时产生大量日志垃圾邮件。

我的方法通常需要几轮修改来摆脱极端情况,但在我看来比编写OTP分布式应用程序更麻烦。此方法与分布式应用程序相比具有另一个优势,因为您无需静态配置群集中涉及的节点列表 - 任何节点都可以作为运行进程主副本的候选节点。你的方法具有相同的属性。

答案 1 :(得分:4)

如何将gen_server转换为应用程序并使用distributed applications

相关问题