分布式Erlang:multicall超出请求的超时

时间:2017-11-12 15:31:36

标签: erlang distributed-computing rpc otp

我们使用分布式erlang集群,现在我在网络分裂的情况下测试它。

要从集群的所有节点获取信息,我使用gen_server:multicall / 4并定义了超时。我需要的是尽快从可用节点获取信息。所以超时不是太大(约3000毫秒)。 这里叫做例子:

Timeout = 3000
Nodes = AllConfiguredNodes
gen_server:multi_call(Nodes, broker, get_score, Timeout)

我希望此调用在Timeout ms中返回结果。但是在净拆分的情况下,它没有。等待约。 8秒。

在发送请求之前,我发现multi_call请求在呼叫erlang:monitor(process, {Name, Node})中暂停了5秒钟。

我真的不在乎某些节点没有回复或忙或不可用,我可以使用任何其他但是这个停止我被迫等到Erlang VM 尝试建立与死/不可用节点的新连接。

问题是:你知道可以阻止这种停顿的解决方案吗?或者可能是另一个适合我情况的RPC。

2 个答案:

答案 0 :(得分:0)

我不确定我是否完全理解您要解决的问题,但如果要获得可以在X时间内检索到的所有答案而忽略其余的答案,您可以尝试async_call的组合和nb_yield。

也许像

somefun() ->
    SmallTimeMs = 50,
    Nodes = AllConfiguredNodes,
    Promises = [rpc:async_call(N, some_mod, some_fun, ArgList) || N <- Nodes],
    get_results([], Promises, SmallTimeMs).


get_results(Results, _Promises, _SmallTimeMs) when length(Results) > 1 ->   % Replace 1 with whatever is the minimum acceptable number of results
    lists:flatten(Results);
get_results(Results, Promises, SmallTimeMs) ->
    Rs = get_promises(Promises, SmallTimeMs)
    get_results([Results|Rs], Promises, SmallTimeMs)).


get_promise(Promises, WaitMs) ->
    [rpc:nb_yield(Key, WaitMs) || Key <- Promises].

有关详细信息,请参阅:http://erlang.org/doc/man/rpc.html#async_call-4

答案 1 :(得分:0)

我解决问题的方法。

我自己实现了使用gen_server:call的multicall 基本思想是在单独的进程中使用gen_server:call()调用所有节点。并收集这些电话的结果。通过从调用进程的邮箱接收消息来进行收集。

要控制超时,我会计算超时到期时的截止日期,然后将其用作参考点来计算afterreceive的超时。

<强>实施

主要功能是:

multicall(Nodes, Name, Req, Timeout) ->
    Refs = lists:map(fun(Node) -> call_node(Node, Name, Req, Timeout) end, Nodes),
    Results = read_all(Timeout, Refs),
    PosResults = [ { Node, Result } || { ok, { ok, { Node, Result } } } <- Results ],
    { PosResults, calc_bad_nodes(Nodes, PosResults) }.

这里的想法是调用所有节点并在一个超时内等待所有结果。

从生成的进程执行调用一个节点。它会捕获gen_server:call在出现错误时使用的出口。

call_node(Node, Name, Req, Timeout) ->
    Ref = make_ref(),
    Self = self(),
    spawn_link(fun() ->
                       try
                           Result = gen_server:call({Name,Node},Req,Timeout),
                           Self ! { Ref, { ok, { Node, Result } } }
                       catch
                           exit:Exit ->
                               Self ! { Ref, { error, { 'EXIT', Exit } } }
                       end
               end),
    Ref.

错误的节点计算为未在Timout内响应的节点

calc_bad_nodes(Nodes, PosResults) ->
    { GoodNodes, _ } = lists:unzip(PosResults),
    [ BadNode || BadNode <- Nodes, not lists:member(BadNode, GoodNodes) ].

通过使用超时

读取邮箱来收集结果
read_all(ReadList, Timeout) ->
    Now = erlang:monotonic_time(millisecond),
    Deadline = Now + Timeout,
    read_all_impl(ReadList, Deadline, []).

执行将在截止日期未发生之前进行读取

read_all_impl([], _, Results) ->
    lists:reverse(Results);
read_all_impl([ W | Rest ], expired, Results) ->
    R = read(0, W),
    read_all_impl(Rest, expired, [R | Results ]);
read_all_impl([ W | Rest ] = L, Deadline, Results) ->
    Now = erlang:monotonic_time(millisecond),
    case Deadline - Now of
        Timeout when Timeout > 0 ->
            R = read(Timeout, W),
            case R of
                { ok, _ } ->
                    read_all_impl(Rest, Deadline, [ R | Results ]);
                { error, { read_timeout, _ } } ->
                    read_all_impl(Rest, expired, [ R | Results ])
            end;
        Timeout when Timeout =< 0 ->
            read_all_impl(L, expired, Results)
    end.

只有通过超时邮箱接收一次读取。

read(Timeout, Ref) ->
    receive
        { Ref, Result } ->
            { ok, Result }
    after Timeout ->
            { error, { read_timeout, Timeout } }
    end.

进一步改进:

  • rpc模块产生单独的进程以避免后期回答的垃圾。因此,在这个多功能函数中执行相同操作
  • 会很有用
  • infinity超时可能会以明显的方式处理