Elixir - 基本主管设置崩溃而不是重新启动子进程

Posted

技术标签:

【中文标题】Elixir - 基本主管设置崩溃而不是重新启动子进程【英文标题】:Elixir - Basic supervisor setup crashes instead of restarting the child process 【发布时间】:2016-04-20 11:04:28 【问题描述】:

忽略 Mix 配置文件的缺失,我写如下:

defmodule Test.Supervisor do
    use Supervisor

    def start_link do
      #"name:" will show up in :observer...
        Supervisor.start_link(__MODULE__, [], [name: :"root_supervisor"])
    end

    def init(args) do
        children = [
            worker(Test.Method, [], [function: :start, id: "my_root_process"]),
        ]

        supervise(children, [strategy: :one_for_one, name: :root])
    end
end

defmodule Test do
    def start(_type, _args) do
        Test.Supervisor.start_link()
    end
end

defmodule Test.Method do
    def start do
        IO.puts("Expect to see me often... #self")
    end
end

第一次运行(iex -S mix)后没有重新启动应用程序就崩溃了。错误信息是:

=INFO REPORT==== 14-Jan-2016::22:34:04 ===
    application: logger
    exited: stopped
    type: temporary
** (Mix) Could not start application mememe: Test.start(:normal, ) returned
an error: shutdown: failed to start child: "my_root_process"
    ** (EXIT) :ok

但是,如果我将Test.start() 更改为直接调用Test.Method.start(),如下所示:

defmodule Test do
    def start(_type, _args) do
        Test.Method.start()
    end
end

然后它运行良好,但代码将不受监督。 我很确定我在实施或理解中犯了一个基本错误,但这个错误到底是什么?

【问题讨论】:

【参考方案1】:

您的代码存在几个问题。首先,你需要一个长时间运行的函数来监督。比如:

def loop do
  receive do
    _anything -> IO.puts "Expect to see me often"
  end
  loop
end

然后在Test.Method 模块中,你必须生成它。

def start do
  IO.puts("Starting...")
  pid = spawn_link(&loop/0)
  :ok, pid
end

重要的是,启动函数返回元组:ok, pid_to_supervise。它使您的应用程序崩溃,因为主管希望有一个进程进行监控,但只得到了 IO.puts 返回的原子 :ok。工作人员规范不会产生新进程。它需要一个函数,该函数将返回生成进程的 pid。

您还应该将主管链接到受监督的进程,因此最后最好将函数重命名为 start_link,而不是 @Jason Harrelson 建议的 start

这应该足以正确启动您的项目。请注意,您不会在观察者Applications 部分中看到您的进程。您没有使用 Application 行为,因此您的 root_supervisor 将漂浮在某处。您可以在Processes 选项卡中找到它。 my_root_process 是与主管一起使用的 id,因此即使在 Processes 选项卡中也不可见。

以这种方式生成过程很容易用于教育目的,但在现实世界的系统中,您希望您的过程遵循 OTP 设计原则。这意味着reacting to system messages,更好的是logging, tracing and debugging。制作满足所有要求的过程非常困难,但您不必手动完成。所有行为都为您执行这些原则。

因此,不要使用循环生成进程,而是尝试使用GenServer

【讨论】:

【参考方案2】:

我会尝试将 Test.Method.start 函数更改为 Test.Method.start_link 函数,并停止在您对工作函数的选择中使用 function: :start。主管默认调用start_link,没有理由破坏这些语义,因为主管将始终链接到受监督的进程。如果这不起作用,那么至少我们已经排除了这方面的问题。

【讨论】:

以上是关于Elixir - 基本主管设置崩溃而不是重新启动子进程的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Elixir 主管中引用之前启动的进程

退出的主管

Elixir:通过某些操作重新启动 GenServer

从主管重新启动后向参与者发送消息

重新启动 => erlang 主管的瞬态与永久

Elixir Redix 基于名称的池示例 - 主管签名不存在