我有一个混合项目,其中包含尽可能简单的Supervisor和GenServer。当我从iex致电时:
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
:mumble调用引发异常,然后重新启动GenServer,第二个:echo调用正常。
如果我以任何其他方式运行代码,Supervisor将无法重新启动GenServer。例如,我使用以下主要模块创建项目的脚本:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
end
end
:mumble调用引发异常,并且escript终止而无需主管重新启动GenServer。
我没有包括Supervisor和Server模块代码,因为从iex调用时它们可以正常工作,所以我猜这里不需要它们。
我在概念上有误解吗?这是不可能的,还是我做错了?
问题不在于服务器和主管,而在于您调用它们的方式。如果在另一个进程正在等待对GenServer.call
的答复时服务器退出,则调用进程也会退出,因此最后一次调用将永远不会发生。这样做的原因是,如果同步调用失败(GenServer.call
是同步的,而不是GenServer.cast
),则该进程不可能在无效状态下继续。如果您这样做只是为了测试主管,则可以尝试:
defmodule EchoCmd.Echo do
def main(args) do
EchoCmd.Supervisor.start_link([:Hello])
GenServer.cast(:echoserver, :echo)
GenServer.cast(:echoserver, :mumble)
GenServer.cast(:echoserver, :echo)
end
end
在iex
中起作用的原因是iex
捕获了出口并允许您键入另一行。
脚本行为正确。您只是错过了iex shell如何“帮助您”。
您在代码中所做的事情正在启动linked进程,而不是将其崩溃。并且由于它是一个链接的进程,因此当它出现故障时,它应该关闭所有链接的进程。可能会有一些“例外”,但是您的脚本过程正在发生什么。
外壳程序管理器和过程管理器都可以处理这样的“我死了,你也应该死”的消息。他们通过更改流程(链接流程,而不是濒临死亡的流程)处理此类消息的方式来做到这一点。它允许他们将它们作为普通消息接收(如果需要,可以在receive
子句中接收),而不是特殊的内部消息。要更改此行为,请使用Process.flag( :trap_exit, :true)
(elixir doc指向eralng's one)。它允许外壳程序仅打印已终止进程的死亡,而不是每次执行不良操作时都会死亡。
所以您可以做同样的事情。更改此标志,并且如果您不希望在此类消息的receive
中进行模式匹配。但我认为这不是您想要的。由于您的过程是单例的,并且主管会全部重新启动,因此您实际上没有任何理由首先链接到它。无需更新有关死亡和重启的信息,只需让主管担心。就像乔·阿姆斯特朗(Joe Armstrong)所说(可能是措辞)
您无需知道如何修理自动售货机即可使用它。
所以,只是start
,而不是start_link
。
就是说,您可能考虑与超级用户创建链接,超级链接在重启过多后也可能会消失(可能会告诉他以这种方式运行)。它使您可以在死时采取他(和受监督的程序)。或者他可以链接到您的主管或应用程序主管,或以其他方式链接。这取决于您的域,并且没有错误的决定,您只需要检查对您有用的。这是设计决定,您必须尝试或阅读有关它的更多信息:
http://elixir-lang.org/getting_started/mix_otp/5.html