handle_call 中发生的陷阱进程崩溃
Trap process crash which occurs in handle_call
我有 2 个 GenServer 模块 - A 和 B。B 监视 A 并实施 handle_info
以在 A 崩溃时接收 :DOWN
消息。
在我的示例代码中,B 向 A 发出同步请求 (handle_call
)。在处理请求时,A 崩溃了。 B 应该收到 :DOWN
消息,但它没有。为什么?
当我用handle_cast
替换handle_call
时,B收到了:DOWN
消息。你能告诉我为什么 handle_call
不起作用而 handle_cast
起作用吗?
这是简单的示例代码:
defmodule A do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok, name: :A)
end
def fun(fun_loving_person) do
GenServer.call(fun_loving_person, :have_fun)
end
def init(:ok) do
{:ok, %{}}
end
def handle_call(:have_fun, _from, state) do
######################### Raise an error to kill process :A
raise "TooMuchFun"
{:reply, :ok, state}
end
end
defmodule B do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok, name: :B)
end
def spread_fun(fun_seeker) do
GenServer.call(:B, {:spread_fun, fun_seeker})
end
def init(:ok) do
{:ok, %{}}
end
def handle_call({:spread_fun, fun_seeker}, _from, state) do
######################### Monitor :A
Process.monitor(Process.whereis(:A))
result = A.fun(fun_seeker)
{:reply, result, state}
rescue
_ -> IO.puts "Too much fun rescued"
{:reply, :error, state}
end
######################### Receive :DOWN message because I monitor :A
def handle_info({:DOWN, _ref, :process, _pid, _reason}, state) do
IO.puts "============== DOWN DOWN DOWN =============="
{:noreply, state}
end
end
try do
{:ok, a} = A.start_link
{:ok, _b} = B.start_link
:ok = B.spread_fun(a)
rescue
exception -> IO.puts "============= #{inspect exception, pretty: true}"
end
In my example code, B makes synchronous request (handle_call) to A. While processing the request, A crashes. B is supposed to receive :DOWN message but it doesn't. Why?
B 在 A 崩溃时确实收到了 :DOWN
消息,但是因为您仍在调用 A 的处理程序中,所以它没有机会处理 :DOWN
消息,直到handle_call
回调完成。但是它永远不会完成,因为调用将因退出而失败,这也会使 B 崩溃。
When I replaced handle_call with handle_cast, B received :DOWN message. Can you please tell me why handle_call doesn't work whereas handle_cast does?
调用是同步的,转换是异步的,所以在这种情况下,转换到 A 的 handle_call
回调完成,然后 B 可以自由处理 :DOWN
消息。 B 不会崩溃,因为强制转换会隐式忽略尝试发送消息时的任何失败,它是 "fire and forget".
在我看来,您正在尝试处理调用 A 时崩溃的问题,这很简单,就像这样:
def handle_call({:spread_fun, fun_seeker}, _from, state) do
######################### Monitor :A
Process.monitor(Process.whereis(:A))
result = A.fun(fun_seeker)
{:reply, result, state}
catch
:exit, reason ->
{:reply, {:error, reason}, state}
rescue
_ ->
IO.puts "Too much fun rescued"
{:reply, :error, state}
end
这将捕获远程进程不活动、死亡或超时时发生的退出。您可以通过在 catch
子句中指定要防止的原因来匹配特定的退出原因,例如 :noproc
。
我不清楚你是否需要显示器,我想这取决于你想用它做什么,但在你给出的例子中我会说你不需要。
我有 2 个 GenServer 模块 - A 和 B。B 监视 A 并实施 handle_info
以在 A 崩溃时接收 :DOWN
消息。
在我的示例代码中,B 向 A 发出同步请求 (handle_call
)。在处理请求时,A 崩溃了。 B 应该收到 :DOWN
消息,但它没有。为什么?
当我用handle_cast
替换handle_call
时,B收到了:DOWN
消息。你能告诉我为什么 handle_call
不起作用而 handle_cast
起作用吗?
这是简单的示例代码:
defmodule A do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok, name: :A)
end
def fun(fun_loving_person) do
GenServer.call(fun_loving_person, :have_fun)
end
def init(:ok) do
{:ok, %{}}
end
def handle_call(:have_fun, _from, state) do
######################### Raise an error to kill process :A
raise "TooMuchFun"
{:reply, :ok, state}
end
end
defmodule B do
use GenServer
def start_link do
GenServer.start_link(__MODULE__, :ok, name: :B)
end
def spread_fun(fun_seeker) do
GenServer.call(:B, {:spread_fun, fun_seeker})
end
def init(:ok) do
{:ok, %{}}
end
def handle_call({:spread_fun, fun_seeker}, _from, state) do
######################### Monitor :A
Process.monitor(Process.whereis(:A))
result = A.fun(fun_seeker)
{:reply, result, state}
rescue
_ -> IO.puts "Too much fun rescued"
{:reply, :error, state}
end
######################### Receive :DOWN message because I monitor :A
def handle_info({:DOWN, _ref, :process, _pid, _reason}, state) do
IO.puts "============== DOWN DOWN DOWN =============="
{:noreply, state}
end
end
try do
{:ok, a} = A.start_link
{:ok, _b} = B.start_link
:ok = B.spread_fun(a)
rescue
exception -> IO.puts "============= #{inspect exception, pretty: true}"
end
In my example code, B makes synchronous request (handle_call) to A. While processing the request, A crashes. B is supposed to receive :DOWN message but it doesn't. Why?
B 在 A 崩溃时确实收到了 :DOWN
消息,但是因为您仍在调用 A 的处理程序中,所以它没有机会处理 :DOWN
消息,直到handle_call
回调完成。但是它永远不会完成,因为调用将因退出而失败,这也会使 B 崩溃。
When I replaced handle_call with handle_cast, B received :DOWN message. Can you please tell me why handle_call doesn't work whereas handle_cast does?
调用是同步的,转换是异步的,所以在这种情况下,转换到 A 的 handle_call
回调完成,然后 B 可以自由处理 :DOWN
消息。 B 不会崩溃,因为强制转换会隐式忽略尝试发送消息时的任何失败,它是 "fire and forget".
在我看来,您正在尝试处理调用 A 时崩溃的问题,这很简单,就像这样:
def handle_call({:spread_fun, fun_seeker}, _from, state) do
######################### Monitor :A
Process.monitor(Process.whereis(:A))
result = A.fun(fun_seeker)
{:reply, result, state}
catch
:exit, reason ->
{:reply, {:error, reason}, state}
rescue
_ ->
IO.puts "Too much fun rescued"
{:reply, :error, state}
end
这将捕获远程进程不活动、死亡或超时时发生的退出。您可以通过在 catch
子句中指定要防止的原因来匹配特定的退出原因,例如 :noproc
。
我不清楚你是否需要显示器,我想这取决于你想用它做什么,但在你给出的例子中我会说你不需要。