Elixir 中的动态主管规范
Dynamic Supervisor Spec in Elixir
我创建了一个 GameSupervisor 监督模块,用于动态创建实例 GameServer (GenServer) 的子项。我可以看到调用 GameSupervisor.start 函数时调用了 GameServer 的 start_link 方法,但它不会使 pid 保持活动状态。如果重启策略设置为临时,Process.alive?(pid) in iex 总是 return false。如果我将重启设置为瞬时或永久重启,当我在该 pid 上调用 GenServer.cast 时,它最终会再次调用 GameServer.start_link。
调用start_child不会自动将pid添加到监督树并保持存活吗?
GameSupervisor.ex
defmodule Prest.GameSupervisor do
alias Prest.GameServer
use Supervisor
@name :game_sup
def start_link() do
IO.puts "start link"
Supervisor.start_link(__MODULE__, [], [name: @name])
end
def start(uid) do
IO.puts "start bucket"
{:ok, child} = Supervisor.start_child(@name, [uid])
end
def init([]) do
IO.puts "init sup"
children = [
worker(GameServer, [], restart: :transient)
]
supervise(children, strategy: :simple_one_for_one)
end
end
GameServer.ex
defmodule Prest.GameServer do
use GenServer
# Client API
def start_link(uid) do
IO.puts "start game server"
GenServer.start_link(__MODULE__, uid, [])
end
def post(pid, event_id) do
:gen_server.cast(pid, {:event, event_id})
end
# Server API
def init(uid) do
{:ok, {uid, [], []}}
end
def handle_cast({:event, event_id}, state) do
#state = [event_id|state]
{:noreply, "ok", state}
end
end
谢谢
根据the docs:
- A
:permanent
进程总是重新启动,即使它正常终止也是如此。
- 一个
:transient
进程只有在异常终止时才会重新启动。
- 一个
:temporary
进程永远不会重新启动。
更有可能的是,您的 GameServer
进程由于某种原因而崩溃,主管正在按照配置的方式处理重启。
要对此进行调试,您需要检查日志(可能只输出到终端)以查看进程崩溃的原因。如果您能够为它获得 {:ok, pid}
,那么它可能不会在初始化时崩溃,这意味着 handle_cast
、handle_call
或 handle_info
子句负责崩溃。
没有代码,很难提供更具体的帮助。
在这种情况下,您的 handle_cast
值似乎有误 return。它 should return {:noreply, state}
,但是 returning {:noreply, "ok", state}
。这通常是由于将 handle_call
更改为 handle_cast
而忘记删除回复值造成的。
我创建了一个 GameSupervisor 监督模块,用于动态创建实例 GameServer (GenServer) 的子项。我可以看到调用 GameSupervisor.start 函数时调用了 GameServer 的 start_link 方法,但它不会使 pid 保持活动状态。如果重启策略设置为临时,Process.alive?(pid) in iex 总是 return false。如果我将重启设置为瞬时或永久重启,当我在该 pid 上调用 GenServer.cast 时,它最终会再次调用 GameServer.start_link。
调用start_child不会自动将pid添加到监督树并保持存活吗?
GameSupervisor.ex
defmodule Prest.GameSupervisor do
alias Prest.GameServer
use Supervisor
@name :game_sup
def start_link() do
IO.puts "start link"
Supervisor.start_link(__MODULE__, [], [name: @name])
end
def start(uid) do
IO.puts "start bucket"
{:ok, child} = Supervisor.start_child(@name, [uid])
end
def init([]) do
IO.puts "init sup"
children = [
worker(GameServer, [], restart: :transient)
]
supervise(children, strategy: :simple_one_for_one)
end
end
GameServer.ex
defmodule Prest.GameServer do
use GenServer
# Client API
def start_link(uid) do
IO.puts "start game server"
GenServer.start_link(__MODULE__, uid, [])
end
def post(pid, event_id) do
:gen_server.cast(pid, {:event, event_id})
end
# Server API
def init(uid) do
{:ok, {uid, [], []}}
end
def handle_cast({:event, event_id}, state) do
#state = [event_id|state]
{:noreply, "ok", state}
end
end
谢谢
根据the docs:
- A
:permanent
进程总是重新启动,即使它正常终止也是如此。 - 一个
:transient
进程只有在异常终止时才会重新启动。 - 一个
:temporary
进程永远不会重新启动。
更有可能的是,您的 GameServer
进程由于某种原因而崩溃,主管正在按照配置的方式处理重启。
要对此进行调试,您需要检查日志(可能只输出到终端)以查看进程崩溃的原因。如果您能够为它获得 {:ok, pid}
,那么它可能不会在初始化时崩溃,这意味着 handle_cast
、handle_call
或 handle_info
子句负责崩溃。
没有代码,很难提供更具体的帮助。
在这种情况下,您的 handle_cast
值似乎有误 return。它 should return {:noreply, state}
,但是 returning {:noreply, "ok", state}
。这通常是由于将 handle_call
更改为 handle_cast
而忘记删除回复值造成的。