我有2个GenServer模块 - A和B.当A崩溃时,B监视A并实现handle_info
以接收:DOWN
消息。
在我的示例代码中,B向A发出同步请求(handle_call
)。在处理请求时,A崩溃。 B应该收到:DOWN
消息,但它不会。为什么呢?
当我用handle_call
替换handle_cast
时,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
答案 0 :(得分:3)
当A崩溃时,在我的示例代码中,B向A发出同步请求(handle_call)。在处理请求时,A崩溃。 B应该接收:DOWN消息,但它没有。为什么呢?
B确实会收到:DOWN
消息,但由于您仍然处于对A的呼叫的处理程序中,因此它不会有机会处理:DOWN
消息,直到handle_call
回调完成。虽然它不会完成,因为呼叫将以退出失败,这也会使B崩溃。
当我用handle_cast替换handle_call时,B收到:DOWN消息。你能否告诉我为什么handle_call不起作用而handle_cast呢?
调用是同步的,强制转换是异步的,因此在这种情况下,转换为A的handle_call
回调完成,然后B可以自由处理:DOWN
消息。 B不会崩溃,因为演员隐含地忽略了尝试发送消息的任何失败,它会发射并忘记"。
在我看来,你正试图在打电话时遇到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
这将捕获远程进程未处于活动状态,死亡或超时时发生的退出。您可以通过在:noproc
子句中指定要保护的原因来匹配特定的退出原因,例如catch
。
我不清楚你需要显示器,我想这取决于你想用它做什么,但在你给出的例子中我会说你没有。