正常关闭GenServer

She*_*yar 17 elixir gen-server

我用GenServer编写了一个Elixir应用程序,它在启动时启动外部应用程序并关闭它并在退出时进行其他清理.我在init/1回调中的回调和清理代码中添加了启动功能terminate/2.

init当GenServer开始代码工作正常,并且terminate也被称为方法中,当:stop被手动发送信号,但在意外停机和中断的情况下(如在按下Ctrl + C的情况下)在IEx标志,该终止代码是不叫.


目前,我已经完成了大量的论坛帖子,博客文章和文档,包括:

来自Elixir Docs - GenServers:

如果在没有捕获退出时从任何进程GenServer接收到退出信号(即不是:normal),它将以相同的原因突然退出,因此不会调用terminate/2.请注意,过程确实NOT默认陷阱退出,并且当链接的过程退出或它的节点断开发送一个退出信号.

因此,不保证terminate/2GenServer退出时调用 .出于这些原因,我们通常建议通过使用监控或链接本身在分离的流程中实现重要的清理规则.

但我完全不知道如何获得: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
Run Code Online (Sandbox Code Playgroud)

sas*_*ric 11

为了增加terminate调用回调的机会,服务器进程应该捕获退出.但是,即使这样,在某些情况下也可能不会调用回调(例如,当进程被残酷地杀死时,或者当它自身崩溃时).欲了解更多详情,请参见这里.

如上所述,如果你想礼貌地关闭你的系统,你应该调用:init.stop,它会递归关闭监督树,导致terminate调用回调.

正如您所注意到的,无法从内部捕获突然的BEAM OS进程退出.它是一个自定义属性:BEAM进程突然终止,因此它无法运行任何代码(因为它终止).因此,如果BEAM被残酷终止,则不会调用回调.

如果您在BEAM死亡时无条件地想要做某事,则需要从另一个OS进程中检测到这一点.我不确定你的确切用例是什么,但假设你有一些强烈的需求,那么在同一个(或另一个)机器上运行另一个BEAM节点可以在这里工作.然后,您可以在一个节点上有一个进程监视另一个节点上的另一个进程,因此即使BEAM被残酷杀死也可以做出反应.

但是,如果您不需要无条件地运行某些清理逻辑,那么您的生活会更简单,因此请考虑代码terminate是必须的,还是更好的.


vfs*_*aki 5

我可以建议你两个解决方案.

第一个在文档中提到.

请注意,进程不会捕获出口.

您必须使您的gen服务器进程陷阱退出.去做这个:

Process.flag(:trap_exit, true)
Run Code Online (Sandbox Code Playgroud)

这会terminate/2在退出时调用您的进程.

但另一种解决方案是将此初始化移交给上层主管.然后让主管将外部应用程序引用传递给gen服务器.但是在这里,terminate如果有必要,你没有退出外部应用程序的回调函数.当主管停止时,外部应用程序将被杀死.