2 个节点之间的 BEAM 行为 RPC 调用

BEAM behaviour RPC call between 2 nodes

我有2个节点,

暂且称他们为A和B吧。

B 有一个 GenServer 模块,启动时监控 A。(此模块仅存在于 B 上) 当A连接到B时,这个GenServer由A启动。

如果 A 在连接到 B 时死亡,B 应该使用 :init.stop()

自杀

这是 GenServer 的代码:

defmodule Monitor do
  use GenServer

  def start_link() do
    GenServer.start_link(__MODULE__, [], [])
  end

  def init([]) do
    {:ok, %{}, 0}
  end

  def handle_info(:timeout, s) do
    start(:"A@127.0.0.1")
    {:noreply, s}
  end
  def handle_info({:nodedown, node}, state) do
    s_node = node |> to_string
    case s_node do
      "A" <> _ ->
        IO.puts "A is down, killing myself !"
        :init.stop()
      _ ->
        :ko
    end
    {:noreply, state}
  end
  def handle_info(_, s) do
    {:noreply, s}
  end

  def start(node) do
    res = Node.monitor(node, true)
    IO.puts "Starting to monitor: #{inspect node}"
  end
end

我同时启动节点 A 和 B。我将 A 连接到 B。我在 A 中使用此命令启动监视器:

> :rpc.call(:"B@127.0.0.1", Monitor, :start_link, [])
{:ok, #PID<8440.594.0>}

如果我使用 Node.disconnect 从 B 正常断开 A

但是,如果我用 Ctrl-C Ctrl-C 甚至 Ctrl-g / q 杀死 A 的控制台 B 上 pid <0.594.0> 的 GenServer 不再存在,因此无法检测到 A 已关闭。 由于某种原因,Pid "linked" 是 A 吗?

PS 我尝试使用 Node.spawn 并在 :rpc.call 中调用 spawn,我得到相同的结果

PS 2 如果我从 B 控制台启动 GenServer,当使用 Node.disconnectCtrl-C Ctrl-C 杀死 A 时,它会按预期工作...

PS 3 我认为这可能来自我调用 :start_link 的事实,但我与 :start 有相同的行为(没有 link)

补充问题 Hynek -Pichi- Vychodil 在评论中回答...

为什么所有 IO.puts 都打印在 A 上,而 GenServer 在技术上 运行 打印在 B 上?

当您调用 :rpc.call(:"B@127.0.0.1", Monitor, :start_link, []) 时,您 link 您的 Monitor 进程到当前 shell(并将 group_leader 设置为 :userA 处处理,这基本上会阻止你在 A 关闭后看到输出 IO.puts "A is down, killing myself !"。)所以这意味着当 A 死亡时,A 处的 shell 运行 也会死亡,你的 Monitor 也是因为 linked。 所以首先,你应该使用 GenServer.start() 作为开始 :rpc.call() 或将进程标志 :trap_exit 设置为 true 或将其放在节点 B 的应用程序 运行 的主管树中. 第二种,在节点B处设置group_leaderuser进程

P.S.: 我不熟悉 Elixir 语法所以我不提供代码。