正常关闭GenServer

时间:2016-09-28 20:08:31

标签: elixir gen-server

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

启动GenServer时init代码正常工作,手动发送terminate信号时也调用:stop方法,但出现意外情况时在IEx中关闭和中断(如按Ctrl + C的情况),不会调用终止代码。

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

  

来自Elixir Docs - GenServers

     

如果GenServer收到退出信号(不是:normal)   从任何进程中,当它没有捕获退出时,它将突然退出   出于同样的原因,请不要拨打terminate/2。注意一个   进程默认情况下 NOT 陷阱退出并发送退出信号   当链接进程退出或其节点断开连接时。

     

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

但我完全不知道如何获得:init.stoplinked 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

2 个答案:

答案 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那样的回调退出外部应用程序。当主管停止时,外部应用程序将被杀死。