7

环境:

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

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

问题):

  • 是否可以检查进程是否已经在 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).

4

2 回答 2

6

如果你返回 {ok, Pid} 的东西你没有链接到它会混淆依赖于返回值的主管。如果您不打算让主管使用它作为 start_link 功能,您可以摆脱它。

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

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

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

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

于 2010-12-16T19:59:15.897 回答
4

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

于 2010-12-16T12:25:10.787 回答