我有一个混合项目,尽可能简单的Supervisor和GenServer。当我从iex打电话时:
EchoCmd.Supervisor.start_link([:Hello])
GenServer.call(:echoserver, :echo)
GenServer.call(:echoserver, :mumble)
GenServer.call(:echoserver, :echo)
:mumble call引发异常,然后GenServer重新启动,第二个:echo调用正常。
如果我以任何其他方式运行代码,Supervisor将无法重新启动GenServer。例如,我使用主模块创建项目的escript,如下所示:
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 call引发异常,escript终止,而Supervisor没有重启GenServer。
我没有包含Supervisor和Server模块代码,因为它们在从iex调用时工作正常,所以我猜这里不需要它们。
我是否存在概念上的误解?这是不可能的,还是我做错了什么?
答案 0 :(得分:5)
问题不在于您的服务器和主管,而在于您调用它们的方式。如果服务器退出而另一个进程正在等待对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
陷阱退出并允许您输入另一行。
答案 1 :(得分:3)
Escript行为是正确的。你只是想念iex shell如何“帮助你”。
您在代码中执行的操作是启动链接进程,而不是崩溃。而且由于它是一个链接过程,当它关闭时,它会假设关闭所有链接过程。可能会有一些“例外”,但是你的escript过程会发生什么。
shell和prcess主管都可以处理这样的“我死了,所以你应该”的消息。他们通过改变流程(链接流程,而不是垂死流程)处理此类消息的方式来实现。它允许他们接收它们作为普通消息(如果您愿意,可以在receive
子句中接收)而不是特殊的内部消息。要更改此行为,请使用Process.flag( :trap_exit, :true)
(elixir doc指向eralng's one)。它允许shell只打印被杀死进程的死亡,而不是每次做坏事时死亡。
所以你可以做同样的事情。更改此标志,如果您不想在receive
上对此类消息进行模式匹配。但我不认为这就是你要找的东西。由于您的进程是单例,并且主管完成所有重新启动,因此您没有任何理由首先链接到它。没有必要更新死亡和重启,只需让主管担心。这就像乔阿姆斯特朗所说(可能是释义)
您无需知道如何修理自动售货机以使用它。
所以,start
,而不是start_link
。
也就是说,您可能会考虑与主管建立链接,主管也可能在重启太多后死亡(他可能会被告知以这种方式行事)。它可以让你在死后接受他(和受监督的过程)。或者他可以链接到您的主管或应用程序主管,或以其他方式。这取决于您的域名,并且没有错误的决定,您只需要检查哪些内容适合您。这是设计决定,你要么必须试验或阅读更多关于它的信息:
http://elixir-lang.org/getting_started/mix_otp/5.html