Elixir GenServer的正常关闭
我正在用GenServer编写一个Elixir应用程序,它在引导时启动一个外部应用程序,然后关闭它,并在退出时进行其他清理。我在回调中添加了启动功能,在回调中添加了清理代码 当GenServer启动时,Elixir GenServer的正常关闭,elixir,gen-server,Elixir,Gen Server,我正在用GenServer编写一个Elixir应用程序,它在引导时启动一个外部应用程序,然后关闭它,并在退出时进行其他清理。我在回调中添加了启动功能,在回调中添加了清理代码 当GenServer启动时,init代码工作正常,当手动发送:stop信号时,terminate方法也被调用,但在IEx中出现意外关机和中断(如按下Ctrl+C)的情况下,不会调用终止代码。 目前,我浏览了大量论坛帖子、博客帖子和文档,包括: 发件人: 如果GenServer接收到退出信号(这不是:normal)
init
代码工作正常,当手动发送:stop
信号时,terminate
方法也被调用,但在IEx中出现意外关机和中断(如按下Ctrl+C)的情况下,不会调用终止代码。
目前,我浏览了大量论坛帖子、博客帖子和文档,包括:
GenServer
接收到退出信号(这不是:normal
)
在任何进程中,当它没有捕获退出时,它将突然退出
出于同样的原因,因此不要调用终止/2
。请注意
默认情况下,进程不会捕获退出,并发送退出信号
当链接进程退出或其节点断开连接时
因此,不能保证在调用时调用terminate/2
GenServer
退出。出于这些原因,我们通常建议
在单独的流程中通过使用
通过链接本身进行监控
但是我完全不知道如何使用:init.stop
,链接进程
或其他任何东西(因为这是我第一次使用GenServer)
这是我的代码:
defmodule MyAwesomeApp do
使用GenServer
def启动do
GenServer.start\u链接(\u模块\u,无)
结束
def初始(状态)do
#做一些启动的事情
IO.puts“开始:{inspect(state)}”
{:好的,州政府}
结束
def终止(原因、状态)do
#做关闭的事情
IO.puts“正在下降:{inspect(state)}”
:正常
结束
结束
MyAwesomeApp.start
我可以向您推荐两种解决方案
第一个在文档中提到
请注意,进程不会捕获出口
您必须使gen server进程陷阱退出。为此:
Process.flag(:trap_exit, true)
这会使您的进程调用在退出时终止/2
但另一个解决方案是将此初始化交给上级主管。然后让主管将外部应用程序引用传递给gen server。但在这里,如果需要退出外部应用程序,您没有类似于终止的回调。当监控程序停止时,外部应用程序将被终止。要增加调用
终止
回调的机会,服务器进程应陷阱退出。但是,即使这样,在某些情况下(例如,当进程被残忍地杀死时,或者当它自己崩溃时),也可能不会调用回调。有关更多详细信息,请参阅
如前所述,如果您想礼貌地关闭系统,您应该调用:init.stop
,这将递归地关闭监控树,从而导致调用终止
回调
正如你们所注意到的,并没有办法从内部捕捉到突然的光束。这是一个自定义属性:如果您试图让BEAM进程在iex和进程中工作,BEAM进程将突然终止,因此它无法运行任何代码(因为它已终止),确保您使用的是
GenServer.start
而不是GenServer.start\u link
,否则shell进程将崩溃,陷阱将无关紧要
下面是一个例子:
defmodule Server do
use GenServer
require Logger
def start() do
GenServer.start(__MODULE__, [], [])
end
def init(_) do
Logger.info "starting"
Process.flag(:trap_exit, true) # your trap_exit call should be here
{:ok, :some_state}
end
# handle the trapped exit call
def handle_info({:EXIT, _from, reason}, state) do
Logger.info "exiting"
cleanup(reason, state)
{:stop, reason, state} # see GenServer docs for other return types
end
# handle termination
def terminate(reason, state) do
Logger.info "terminating"
cleanup(reason, state)
state
end
defp cleanup(_reason, _state) do
# Cleanup whatever you need cleaned up
end
end
在iex中,您现在应该看到一个被困的退出呼叫
iex> {:ok, pid} = Server.start()
iex> Process.exit(pid, :something_bad)
Process.flag(:trap\u exit,true)
对我不起作用。您能告诉我在genserver中应该在哪里调用它吗?您应该在过程中设置它,这意味着init/1
似乎问题不在于gen server本身。启动一个iex
shell,Process.flag(:trap\u exit,true)
并编写一个简单的receive
来匹配全部,似乎也不起作用。如果您的问题仅仅是关闭外部应用程序,那么当gen server自动停止时就会完成,但是关于您提到的其他清理,可能会更详细地告诉您将要做什么。>正如您所注意到的,无法从内部捕获操作系统进程退出。自从OTP 20+:处理信息({:退出,与终止/2有什么区别?