GenServer的terminate callback在進程退出時會被調用.
但若沒有:erlang.process_flag(:trap_exit, true), 進程可能被悄無聲息地kill掉, 而不走terminate回調.git
gen_server定義了6個callback接口:github
init/1 handle_call/3 handle_cast/2 handle_info/2 terminate/2 code_change/3
對於callback的實現者來講,理解callback函數的觸發點是最重要的,本文只討論terminate的調用。app
terminate的被調用有以下幾種狀況:函數
設置了一個timeout或者無限等待的狀況下,supervisor是經過exit(Pid, shutdown)來通知子進程退出的,因此,若supervisor下的gen_server worker進程沒有設爲系統進程,worker進程不會收到來自Parent的Exit消息,故terminate不會被調用。ui
App.Supervisor下有一個App.Worker, 代碼以下.code
defmodule App do use Application require Logger def start(_type, _args) do children = [ %{id: App.Supervisor, start: {App.Supervisor, :start_link, []}, type: :supervisor} ] opts = [strategy: :one_for_one, name: __MODULE__] Supervisor.start_link(children, opts) end end defmodule App.Supervisor do use Supervisor require Logger def start_link() do Logger.info("app.sup start") Supervisor.start_link(__MODULE__, [], name: __MODULE__) end def init([]) do children = [ %{id: App.Worker, start: {App.Worker, :start_link, []}, type: :worker} ] opts = [strategy: :one_for_one, name: __MODULE__] Supervisor.init(children, opts) end end defmodule App.Worker do use GenServer require Logger require Record Record.defrecordp(:state, []) def start_link() do Logger.info("app.worker start") GenServer.start_link(__MODULE__, [], name: __MODULE__) end def init(_) do # :erlang.process_flag(:trap_exit, true) {:ok, nil} end def handle_call(:stop, _from, state) do {:stop, nil, state} end def handle_call(:raise, _from, state) do raise RuntimeError {:reply, nil, state} end def terminate(reason, state) do Logger.warn("app.worker terminate #{inspect(reason)} #{inspect(state)}") :ok end end
若沒有:erlang.process_flag(:trap_exit, true).orm
均不會觸發terminate回調.server
會觸發terminate回調.blog
會觸發terminate回調.接口
20:25:00.568 [warn] app.worker terminate :shutdown nil
會收到{:EXIT, #PID<0.149.0>, :normal}消息, 若處理了消息, 不會觸發terminate回調.
20:25:59.849 [warn] unknown_info:{:EXIT, #PID<0.134.0>, :normal}
20:27:55.170 [warn] unknown_info:{:EXIT, #PID<0.134.0>, :shutdown}
會觸發terminate回調.
terminate被調用的方式有以下幾種:
若是但願gen_server進程崩潰時terminate必定被調用到(exit(Pid, kill)除外),設爲system進程便可。
本篇文章部份內容來自於多年前寫的blog, 當時未記錄源碼內容.