【问题标题】:Supervised GenServer not being restarted?受监督的 GenServer 未重新启动?
【发布时间】:2017-01-09 18:30:04
【问题描述】:

我已经缩小了问题的大小,因为它太大了。代码如下:

defmodule MayRaiseGenServer do
  use GenServer

  def start_link do
    IO.puts "started MyServer, name is #{__MODULE__}"

    GenServer.start_link(__MODULE__, [], name: __MODULE__)
  end

  def maybe_will_raise do
    GenServer.call(__MODULE__, :maybe_will_raise)
  end

  def handle_call(:maybe_will_raise,_from, state) do
    IO.puts "maybe_will_raise called!"
    :random.seed(:erlang.now)
    number = Enum.to_list(1..100) |> Enum.shuffle |> List.first
    IO.puts "number is #{number}"
    if rem(number,2) != 0 do
      raise "#{number}"
    end
    {:reply, {"You got lucky"}, state}
  end
end

defmodule MayRaiseSupervisor do
  use Supervisor

  def start_link([]) do
    IO.puts "starting supervisor, name is #{__MODULE__}"
    Supervisor.start_link(__MODULE__, [])
  end

  def init(arg) do
    IO.puts "initted with arg: #{arg}"
    children = [
      worker(MayRaiseGenServer, [])
    ]

    supervise(children, strategy: :one_for_one, restart: :transient, name: __MODULE__)
  end
end

MayRaiseSupervisor.start_link([])
IO.inspect MayRaiseGenServer.maybe_will_raise
:timer.sleep(2000)
IO.puts "after sleep"

一开始只看到一次启动GenServer的提示,现在又看到了。这是输出:

starting supervisor, name is Elixir.MayRaiseSupervisor
initted with arg:
started MyServer, name is Elixir.MayRaiseGenServer
maybe_will_raise called!
number is 14
started MyServer, name is Elixir.MayRaiseGenServer

11:32:28.807 [error] GenServer MayRaiseGenServer terminating
** (RuntimeError) 14
    lib/mini.ex:20: MayRaiseGenServer.handle_call/3
    (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4
    (stdlib) gen_server.erl:647: :gen_server.handle_msg/5
    (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
Last message: :maybe_will_raise
State: []
** (exit) exited in: GenServer.call(MayRaiseGenServer, :maybe_will_raise, 5000)
    ** (EXIT) an exception was raised:
        ** (RuntimeError) 14
            lib/mini.ex:20: MayRaiseGenServer.handle_call/3
            (stdlib) gen_server.erl:615: :gen_server.try_handle_call/4
            (stdlib) gen_server.erl:647: :gen_server.handle_msg/5
            (stdlib) proc_lib.erl:247: :proc_lib.init_p_do_apply/3
    (elixir) lib/gen_server.ex:604: GenServer.call/3
    lib/mini.ex:45: (file)
    (elixir) lib/code.ex:363: Code.require_file/2

从上面的输出中,我不太清楚会发生什么。根据 IO 上显示的消息,看起来 GenServer 已重新启动,但为什么再次抛出异常?另外,在这段代码中:

MayRaiseSupervisor.start_link([])
IO.inspect MayRaiseGenServer.maybe_will_raise
:timer.sleep(2000)
IO.puts "after sleep"

如果方法调用MayRaiseGenServer.maybe_will_raise 确实会引发错误,看起来就像后面的行,带有timer.sleepIO.puts 的行将不再运行。即使我更改代码以尝试处理异常,如下所示:

MayRaiseSupervisor.start_link([])
try do
  IO.inspect MayRaiseGenServer.maybe_will_raise
rescue
  RuntimeError -> IO.puts "there was an error"
end
:timer.sleep(2000)
IO.puts "after sleep"

我似乎仍然无法到达最后一个IO.puts(如果出现错误)。有没有办法处理对maybe_will_raise 的调用,让我能够处理它引发错误并继续执行?我猜主管在重新启动时不会自动重试一段代码。

【问题讨论】:

  • 好吧,根据输出,服务器已重新启动,因为有 2 行带有 started MyServer。您也可以通过调用:Process.alive? Process.whereis(MayRaiseGenServer) 来检查。如果您的服务器正在运行,它将返回 true。
  • 我注意到了不同的 PID,但 IO.puts "started MyServer" 输出应该不会再次出现吗?

标签: elixir erlang-otp


【解决方案1】:

作为我的观点。

上面的输出告诉您一个堆栈跟踪,当一个异常引发时,GenServer.call(MayRaiseGenServer, :maybe_will_raise, 5000) 中的退出信号和一个错误日志,因为 terminate/2 被调用,原因是 {%RuntimeError{message: ...}, [...]

你可以定义terminate/2回调来查看:

def terminate(reason, _state) do
  IO.inspect reason
end

Terminate/2

如果原因不是 :normal, :shutdown 也不是 {:shutdown, term} 错误是 记录。

但是当在 GenServer 回调中引发异常时(init/1 除外),它将调用 terminate/2 告诉服务器即将退出(已发送退出信号)。

所以这行之后的代码不会被执行:

try do
IO.inspect MayRaiseGenServer.maybe_will_raise
...

但是 IO.puts “started MyServer”输出不应该再次出现吗?

还有当您的 GenServer 退出时。您的主管将启动一个新的,将主进程与您的 GenServer 进程链接起来(您的 MayRaiseGenServer.start_link 再次接到电话)

最后一件事是如果你想让代码继续执行。你可以像这样捕获退出信号

MayRaiseSupervisor.start_link([])
try do
  IO.inspect MayRaiseGenServer.maybe_will_raise
catch
  :exit, _ -> IO.puts "there was an error"
end
:timer.sleep(2000)
IO.puts "after sleep"

但我认为您应该考虑在您的 GenServer 回调中使用 raise。希望对您有所帮助!

【讨论】:

    【解决方案2】:

    问题是您的主管没有链接到 GenServer。因此,不会通知孩子的死亡(通过退出信号)。要解决这个问题,您需要使用GenServer.start_link/3

    有关更多信息,请查看ErlangElixir 有关流程的文档。

    【讨论】:

    • 我已经更新了描述。使用start_link 时,我似乎也有同样的行为
    【解决方案3】:

    我误用了GenServer.start而不是GenServer.start_link,搜索了两天的答案。

    注意 1::observer.start 中的主管树对调试此问题有很大帮助

    注意 2: 但上述问题仅使用start_link。我正在为像我一样犯同样错误的人写信:-P。

    【讨论】:

      【解决方案4】:

      服务器正在重新启动。为了证明这一点,我添加了这个 sn -p

      spawn fn ->
        :timer.sleep(1000)
        IO.inspect GenServer.call(__MODULE__, :maybe_will_raise)
      end
      

      在两个地方。一个在handle_call 回调中,我还在init 函数中添加了它。这是之后的完整模块

        defmodule MayRaiseGenServer do
          @moduledoc false
      
          use GenServer
      
          def start_link do
            IO.puts "started MyServer, name is #{__MODULE__}"
      
            GenServer.start_link(__MODULE__, [], name: __MODULE__)
          end
      
          def init(_) do
            spawn fn ->
              :timer.sleep(1000)
              IO.inspect GenServer.call(__MODULE__, :maybe_will_raise)
            end
            {:ok, nil}
          end
      
          def maybe_will_raise do
            GenServer.call(__MODULE__, :maybe_will_raise)
          end
      
          def handle_call(:maybe_will_raise,_from, state) do
            IO.puts "maybe_will_raise called!"
            :random.seed(:erlang.now)
            number = Enum.to_list(1..100) |> Enum.shuffle |> List.first
            IO.puts "number is #{number}"
            if rem(number,2) != 0 do
              raise "#{number}"
            end
            spawn fn ->
              :timer.sleep(1000)
              IO.inspect GenServer.call(__MODULE__, :maybe_will_raise)
            end
            {:reply, {"You got lucky"}, state}
          end
      
      
        end
      

      那你就不用打电话了

      IO.inspect MayRaiseGenServer.maybe_will_raise
      

      之后

      MayRaiseSupervisor.start_link([])
      

      试试看

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-27
        • 2021-03-08
        • 2017-09-10
        • 1970-01-01
        • 2017-08-12
        • 2012-12-17
        • 2017-09-04
        • 2020-06-01
        相关资源
        最近更新 更多