我用GenServer编写了一个Elixir应用程序,它在启动时启动外部应用程序并关闭它并在退出时进行其他清理。我在init/1
回调中的terminate/2
回调和清理代码中添加了启动功能。
启动GenServer时init
代码正常工作,手动发送terminate
信号时也调用:stop
方法,但出现意外情况时在IEx中关闭和中断(如按Ctrl + C的情况),不会调用终止代码。
目前,我已经完成了大量的论坛主题,博客文章和文档,包括:
如果
GenServer
收到退出信号(不是:normal
) 从任何进程中,当它没有捕获退出时,它将突然退出 出于同样的原因,请不要拨打terminate/2
。注意一个 进程默认情况下 NOT 陷阱退出并发送退出信号 当链接进程退出或其节点断开连接时。因此无法保证在a时调用
terminate/2
GenServer
退出。出于这些原因,我们通常建议重要 通过使用的分离过程中发生的清理规则 监控或链接本身。
但我完全不知道如何获得:init.stop
,linked processes
或其他任何可以使用它的东西(因为这是我第一次使用GenServers)。
这是我的代码:
defmodule MyAwesomeApp do
use GenServer
def start do
GenServer.start_link(__MODULE__, nil)
end
def init(state) do
# Do Bootup stuff
IO.puts "Starting: #{inspect(state)}"
{:ok, state}
end
def terminate(reason, state) do
# Do Shutdown Stuff
IO.puts "Going Down: #{inspect(state)}"
:normal
end
end
MyAwesomeApp.start
答案 0 :(得分:10)
为了增加调用terminate
回调的机会,服务器进程应该捕获退出。但是,即使这样,在某些情况下也可能不会调用回调(例如,当进程被残酷地杀死时,或者当它自身崩溃时)。有关详细信息,请参阅here。
如上所述,如果你想礼貌地关闭你的系统,你应该调用:init.stop
,它将递归关闭监督树,导致调用terminate
回调。
正如您所注意到的,无法从内部捕获突然的BEAM OS进程退出。它是一个自定义属性:BEAM进程突然终止,因此它无法运行任何代码(因为它终止)。因此,如果BEAM被残酷终止,则不会调用回调。
如果您在BEAM死亡时无条件地想要做某事,则需要从另一个OS进程中检测到这一点。我不确定您的确切用例是什么,但假设您对此有强烈需求,那么在同一台(或另一台机器)上运行另一个BEAM节点可以在这里工作。然后,您可以在一个节点上有一个进程监视另一个节点上的另一个进程,因此即使BEAM被残酷杀死也可以做出反应。
但是,如果您不需要无条件地运行某些清理逻辑,那么您的生活会更简单,因此请考虑terminate
中的代码是必须的,还是更好的。< / p>
答案 1 :(得分:5)
我可以建议你两个解决方案。
第一个在文档中提到。
请注意,进程不会阻止退出。
您必须使您的Gen服务器进程陷阱退出。要做到这一点:
Process.flag(:trap_exit, true)
这会让您的流程在退出时调用terminate/2
。
但另一种解决方案是将此初始化移交给上层主管。然后让主管将外部应用程序引用传递给gen服务器。但是在这里,如果有必要的话,你没有像terminate
那样的回调退出外部应用程序。当主管停止时,外部应用程序将被杀死。