2

複数のリモートノードのアクティビティを監視する単純なgen_serverモジュールを構築しています

リモートノードが登録されると、このモジュールはerlang:monitor_node(Node、true)でノードを監視します。これはノードごとに1回だけ登録されます(ログで確認)

gen_serverのhandle_info/2コールバックでは、{nodedown、Node}メッセージをキャッチし、erlang:monitor_node(Node、false)でノードを監視解除します。このメッセージは、リモートノードがダウンしているときに一度だけ受信することを期待しています。

モジュールをテストしていたときに、リモートノードがダウンすると、数百の{nodedown、Node}メッセージ(数は数百から数千まで変化します)がgen_serverに送信されることがわかりました。

monitor_nodeによって複数のメッセージが送信されるのはなぜですか?どうすればこの動作を防ぐことができますか?

編集:ここにソースコード(の一部)があります

register_node(#node_info{node = NodeName} = NodeInfo) ->
    case mnesia:read(node_info, NodeName) of
        [] ->
            monitor_node(NodeName, true),
            error_logger:info_msg("node ~p registered", [NodeName]);
        [_OldInfo] ->
            error_logger:trace_msg("info of node ~p updated", [NodeName])
    end,
    mnesia:write(NodeInfo).

handle_cast({register_node, #node_info{} = NodeStatus}, Timer) ->
    case mnesia:transaction(fun register_node/1, [NodeStatus]) of
        {aborted, Reason} ->
            error_logger:warning_msg("transaction register_node failed: ~p", [Reason]);
        _ ->
        ok
    end,
    {noreply, Timer};
handle_cast({shutdown_node, #node_info{} = NodeStatus}, Timer) ->
    case mnesia:dirty_delete_object(NodeStatus) of
        {aborted, Reason} ->
            error_logger:warning_msg("transaction shutdown_node failed: ~p", [Reason]);
        _ ->
        ok
    end,
    {noreply, Timer};
handle_cast(Message, Timer) ->
    error_logger:warning_msg("~p: received unknown message ~p", [?MODULE, Message]),
    {noreply, Timer}.

handle_info({nodedown, Node}, Timer) ->
    monitor_node(Node, false),
    error_logger:info_msg("~p: node ~p down", [?MODULE, Node]),
    mnesia:transaction(fun mnesia:delete/3, [node_info, Node, write]),
    {noreply, Timer};
handle_info(Message, Timer) ->
    error_logger:warning_msg("~p: received unknown message ~p", [?MODULE, Message]),
    {noreply, Timer}.
4

1 に答える 1

5

monitor_node(NodeName, true) **INSIDE**mnesiaトランザクションを実行しました。

monitor_nodeは内部で(I / O操作)メッセージ通信を伴うためだと思います。この行をtransation内に配置することは適切ではありません。関係するノードに大量の'registered'メッセージを送信する可能性があります。そのため、ノードがダウンしたときに、多数の'nodedown'メッセージが受信されました。

    If a process has made two calls to monitor_node(Node, true) and Node terminates, 
**two nodedown messages are delivered to the process.** If there is no connection 
to Node, there will be an attempt to create one. If this fails, a nodedown 
message is delivered.

行を外に移動するtransactionか、式を使用して"CASE"、再試行してください。

register_node(#node_info{node = NodeName} = NodeInfo) ->
    case mnesia:read(node_info, NodeName) of
        [] ->
            monitor_node(NodeName, true),
            error_logger:info_msg("node ~p registered", [NodeName]);
        [_OldInfo] ->
            error_logger:trace_msg("info of node ~p updated", [NodeName])
    end,
    mnesia:write(NodeInfo).
handle_cast({register_node, #node_info{} = NodeStatus}, Timer) ->
    case mnesia:transaction(fun register_node/1, [NodeStatus]) of
        {aborted, Reason} ->
            error_logger:warning_msg("transaction register_node failed: ~p", [Reason]);
        _ ->
        ok
    end,
    {noreply, Timer};

Mnesiaトランザクションの副作用の説明

Mnesiaは、トランザクションの実行時に動的にロックを設定および解放するため、トランザクションの副作用を伴うコードを実行することは非常に危険です。特に、トランザクション内のreceiveステートメントは、トランザクションがハングして戻らない状況につながる可能性があり、 その結果、ロックが解放されない可能性があります。この状況では、他のプロセスまたは他のノードで実行される他のトランザクションが欠陥のあるトランザクションを待機することを余儀なくされるため、システム全体が停止する可能性があります。

于 2012-11-27T09:54:15.430 に答える