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

Rum*_*ilz 6 erlang global gen-server

设置:

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

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

问题(S):

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

示例代码:


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).


arc*_*lus 5

如果您返回{ok,Pid}的内容但未链接到该内容,则会混淆依赖返回值的主管。如果您不想让主管使用它作为start_link函数,则可以摆脱它。

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

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

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

我的方法通常需要经过几轮修订才能解决一些棘手的问题,但是在我看来,这比编写OTP分布式应用程序要麻烦得多。与分布式应用程序相比,此方法还有另一个优势,因为您不必静态配置集群中涉及的节点列表-任何节点都可以作为运行流程主副本的候选对象。您的方法具有相同的属性。


小智 4

将 gen_server 转变为应用程序并使用分布式应用程序怎么样?

  • +1 这是要走的路。如果您想自己构建它:a)在没有竞争条件的情况下很难正确完成;b)您可以以分布式应用程序管理的源代码为例。您可以从 erlang 生成配置文件,例如,如果您不想让节点名称静态,则可以从配置生成器节点查询所有节点。OTOH 当节点是动态的时,查找节点时存在额外的失败可能性(您如何知道您拥有所需的所有节点?) (2认同)