我有2個GenServer模塊 - A和B. B監視A並執行handle_info
以在A崩潰時接收:DOWN
消息。在handle_call中發生陷阱進程崩潰
在我的示例代碼中,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
非常感謝!真棒解釋。 我有一個RabbitMQ消息使用者(模塊B),依賴於另一個模塊(A)。當A崩潰並且B不能拒絕並重新發送消息時,我不希望消息陷入僵局。你知道更好的方法嗎? –
那麼一種方法是讓B在A失敗時丟棄消息,並讓消息過期並自動重新由RabbitMQ重新發送消息(假設隊列已正確配置)。另一種方法是使用上面的「捕捉」策略,在放棄之前重試該信息幾次。另一種方法是讓B將消息傳遞給負責處理該消息的進程,釋放B繼續消費其他消息並讓該進程重試直至成功爲止;但這隻適用於消息順序不重要的情況,然後你有一個流量控制問題來處理 – bitwalker
在我看來,你最好是找到一種方法讓B拒絕/重新發送消息(這當然應該是可行的,如果你正在捕捉退出),或者丟棄失敗的消息,並讓它們自動失效並重新排序。這實際上取決於你需要提供什麼保證,如果消息的順序很重要,重試可能是一個更好的選擇。 – bitwalker