Ich habe 2 GenServer-Module - A und B. B überwacht A und implementiert handle_info
, um :DOWN
Nachricht zu erhalten, wenn A abstürzt.Trap-Prozessabsturz in handle_call
In meinem Beispiel Code, B macht synchrone Anfrage (handle_call
) an A. Während der Bearbeitung der Anfrage stürzt A ab. B soll :DOWN
Nachricht empfangen, tut es aber nicht. Warum?
Wenn ich handle_call
durch handle_cast
ersetzte, erhielt B :DOWN
Nachricht. Können Sie mir bitte sagen, warum handle_call
nicht funktioniert, während handle_cast
tut?
Dies ist die einfache Beispielcode:
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
Vielen Dank! Tolle Erklärung. Ich habe einen RabbitMQ-Nachrichtenkonsumenten (Modul B), der von einem anderen Modul (A) abhängt. Ich möchte nicht, dass Nachrichten in der Schwebe sind, wenn A abstürzt und B die Nachricht nicht ablehnen und zurückfordern kann. Kennen Sie einen besseren Ansatz? –
Nun, ein Ansatz besteht darin, dass B die Nachricht abbricht, wenn A fehlschlägt, und die Nachricht ablaufen lässt und von RabbitMQ automatisch erneut angestellt wird (vorausgesetzt, dass Ihre Warteschlangen entsprechend konfiguriert sind). Eine andere Möglichkeit besteht darin, die "Fang" -Taktik oben zu verwenden und die Nachricht einige Male erneut zu versuchen, bevor Sie aufgeben.Ein weiterer Grund ist, dass B die Nachricht an einen Prozess weitergibt, der für die Verarbeitung dieser Nachricht zuständig ist, wodurch B freigegeben wird, um weitere Nachrichten zu konsumieren, und diesen Prozess wiederholen zu lassen, bis es erfolgreich ist; aber das funktioniert nur, wenn die Reihenfolge der Nachrichten unwichtig ist, und dann haben Sie ein Flow-Control-Problem zu behandeln – bitwalker
Meiner Meinung nach, ist es besser für Sie einen Weg zu finden, B abzulehnen/requeue Nachrichten (die sicherlich machbar sein sollten, wenn Sie können Exits abfangen oder fehlgeschlagene Nachrichten löschen und ablaufen lassen und automatisch neu anfragen. Es hängt wirklich davon ab, welche Garantien Sie liefern müssen, aber eine Wiederholung ist möglicherweise eine bessere Wahl, wenn die Reihenfolge der Nachrichten kritisch ist. – bitwalker