如何通过在 Elixir 中调用进程来捕获或挽救被调用进程的崩溃错误

Posted

技术标签:

【中文标题】如何通过在 Elixir 中调用进程来捕获或挽救被调用进程的崩溃错误【英文标题】:How to catch or rescue called process' crash error by calling process in Elixir 【发布时间】:2018-02-05 23:48:02 【问题描述】:

我一直试图弄清楚如何在调用进程 B 中捕获或挽救另一个进程 A 中的错误,该错误也杀死了进程 A。

这是我的代码:

defmodule A do
  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 "TooMuchFun"
    :reply, :ok, state
  end
end

defmodule B do
  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
    result = A.fun(fun_seeker)
    :reply, result, state
  rescue
    RuntimeError -> IO.puts "Too much fun rescued"
    :reply, :error, state
  end
end

:ok, a = A.start_link
:ok, _b = B.start_link
result = B.spread_fun(a)
IO.puts "#inspect result"

在模块 B 的 handle_call 函数中,我调用了模块 A 的函数,它有 rescue 块,以防进程 :A 出现任何问题。引发了错误,但 rescue 块没有被执行。

我是否错过了对一个进程崩溃如何影响另一个进程的基本了解?仅当错误发生在同一进程中时,try/catch 或 try/rescue 才有效吗?我是否必须监视其他进程并捕获其退出?

感谢您的帮助。

【问题讨论】:

【参考方案1】:

你可以通过让另一个进程监控这个进程来完成你喜欢的事情。查看Process.monitor 的文档:https://hexdocs.pm/elixir/Process.html#monitor/1。

从本质上讲,监控进程需要处理崩溃时生成的宕机消息的信息:

handle_info(:DOWN, ref, :process, object, reason, state) do
  # do something interesting here ...
end

请记住,您需要弄清楚您想要执行此操作的 reason(s) 是什么,并且模式匹配仅包含那些。

【讨论】:

谢谢。我modified my example code。 B 监视 A 并为 :DOWN 消息实现handle_info。但是,B 没有收到 :DOWN 消息。我还在调查。【参考方案2】:

在 Erlang 生态系统中,只有在进程代码中引发异常时,您才可以使用 try-catch 捕获错误、退出和抛出,但如果进程因 atom normal 之外的任何原因退出,所有链接的进程都将收到退出信号,那些捕获退出的进程会以'EXIT', CrashedProcessPid, ReasonOfCrash 的形式将这个信号作为正常的erlang 消息接收。并且没有陷阱退出的其他进程将崩溃,原因为ReasonOfCrash,并且与这些进程链接的其他进程将收到signalz等等。

【讨论】:

谢谢。所以如果我理解你,抛出/引发错误和捕获/救援它必须发生在同一个过程中?

以上是关于如何通过在 Elixir 中调用进程来捕获或挽救被调用进程的崩溃错误的主要内容,如果未能解决你的问题,请参考以下文章

捕获Scala进程失败

如何在 Elixir 主管中引用之前启动的进程

如何在Elixir或Erlang中在运行时动态创建和加载模块?

为啥需要 Elixir 捕获运算符来将函数绑定到值

Elixir/Erlang Random/Rand Seed 是不是需要在每个进程上调用?

Elixir 与 Mix 如何制作守护进程?