【问题标题】:Starting and Terminating child of simple_one_for_one supervisor启动和终止 simple_one_for_one 主管的孩子
【发布时间】:2014-12-09 02:57:39
【问题描述】:

我有一个简单的应用程序target_interceptor,它在接收注册和注销消息时,启动或终止simple_one_for_one rpc_server_supervisor 下的工作人员。

主管rpc_server_supervisor的代码:

init([]) ->
    MaxRestart = 5,
    MaxTime = 3600,
    {ok, {{simple_one_for_one, MaxRestart, MaxTime},
            [{rbmq_rpc_server, 
                {rbmq_rpc_server, start_link, []},
                temporary,
                10000,
                worker,
                [rbmq_rpc_server]}]}}.

target_interceptor的注册留言:

handle_cast({register, Args}, S = #state{channel = Channel, supervisor=Sup, refs=R, qs_link = QSLinks}) ->
    {Connection, QueueName, Node} = Args,   
    {Ok, Pid} = supervisor:start_child(Sup, [Connection, QueueName, Node]),
    Ref = erlang:monitor(process, Pid),
{noreply, S#state{refs=gb_sets:add(Ref,R), qs_link=orddict:append(binary_to_list(QueueName),Pid,QSLinks)}};

target_interceptor的注销消息:

handle_cast({unregister,{QueueName}}, S = #state{supervisor = Sup, qs_link = QSLinks}) ->
    Pid = orddict:fetch(QueueName,QSLinks) 
    case supervisor:terminate_child(Sup,Pid) of
         ok -> Success = true;
         Error -> io:format("Error ~p~n",[Error]),
                  Success = false
    end,
{noreply, S#state{qs_link=orddict:erase(QueueName,QSLinks)}}

我的 Erlang 版本是:R15B01

第一个问题是在处理注册操作时,元组 {OK, Pid} = {error, };尽管它表明出现问题,但 Pid 57 上的 gen_server rbmq_rpc_server 工作正常并响应消息。为什么start_child函数的返回值错误?出了什么问题?

第二个问题是在处理注销操作时,即使我引用 Pid 而不是 ChildID,supervisor:terminate_child(Sup,Pid) 也会返回 {error,simple_one_for_one)。为什么会有这样的行为?我如何动态地单独终止主管的孩子?

编辑: target_interceptorrpc_server_supervisor 均由 rbmq_sup_sup 主管监督:

init({Nodeid, Node}) ->
    MaxRestart = 1,
    MaxTime = 3600,
    {ok, {{rest_for_one, MaxRestart, MaxTime},
            [{server,
                {target_interceptor, start_link, [target, self(), {Nodeid, Node}]},
                permanent,
                5000,
                worker,
                [target_interceptor]}]}}.

编辑: rpc_server_supervisor 在 target_interceptor init() 函数中被调用(这里是 rbmq_sup_sup supervisor):

handle_info({start_worker_supervisor, Sup}, S = #state{}) ->
    {ok, Pid} = supervisor:start_child(Sup, ?SPEC),
    link(Pid),
    {noreply, S#state{sup=Pid}};

-define(SPEC,
        {rpc_server_sup,
            {rpc_server_sup, start_link, []},
            temporary,
            10000,
            supervisor,
            [rpc_server_sup]}).

【问题讨论】:

    标签: erlang erlang-supervisor


    【解决方案1】:

    有时间我会补充的。

    第一点是{Ok,Pid} 匹配任何元组,甚至是{error,Error},因此调用变量 Ok 可能不是最佳选择。

    一个简单的问题:在handle_cast({register,Args}, ... 中,您使用binary_to_list(QueueName) 作为密钥,但在handle_cast({unregister,{QueueName}}, ... 中,您只需使用QueueName 作为密钥。为什么?为什么要为每个 QueueName 保留一个 pid 列表,因为取消注册似乎将它们全部清除?这也意味着当您执行 Pid = orddict:fetch(QueueName,QSLinks) 时,Pid 将是一个 pid 列表,而不是一个。

    rbmq_sup_sup 中,您只需启动target_interceptor

    rbmq_rpc_server 进程是否已注册?

    编辑:

    我认为在执行 unregister 时出错的原因是您使用 orddict:append/3 保存了 pid,即使是第一次,您也可以将值保存在 list 中,而您正在获取orddict:fetch/2 的值,在这种情况下返回整个列表。所以Pid 是一个列表。然后,您尝试使用Pid 杀死孩子,这是一个 pid 列表,而不是 supervisor:terminate_child/2 抱怨的 pid。

    如果每个 QueueName 只有一个 pid,那么您应该改为使用 orddict:store(binary_to_list(QueueName), Pid, QSLinks)。然后orddict:fetch/2 将返回 pid。

    附: orddict:append/3 背后的想法是,您可以向同一个键添加更多值,并保留所有值的列表。

    【讨论】:

    • 感谢您的评论,但这不会引起我的问​​题 1.) {Ok,Pid} 元组仅用于演示 2.) 在寄存器强制转换中,QueueName 具有二进制形式并且在取消注册 - 列表中格式,我应该统一它以提高可读性 3.)我列出 Pids 以便能够在消息到达时取消注册正确的进程。 (un)register msg 通过 rbmq 通道到达 target_interceptor,使用 handle_info(#'basic.deliver',#'amqp_msg') 并使用适当的 QueueNames 强制转换 (un)register 消息
    • 4.) 每个 QueueName 只有一个进程,因此每个 QueueName 都是唯一的,可以通过 Pid 引用终止; 5.) rbmq_rpc_server 在 target_interceptor 初始化函数中启动,因此它在 supervisor 上有一个 ref,并且 target_interceptor 可以在请求时启动/终止 supervisor 的子节点。
    猜你喜欢
    • 2017-05-02
    • 2012-11-04
    • 2012-05-07
    • 2016-06-11
    • 2020-07-22
    • 2023-03-13
    • 1970-01-01
    • 2017-02-21
    • 2016-08-16
    相关资源
    最近更新 更多