2016-09-01 4 views
12

Я уменьшил размер вопроса, потому что он был слишком большим. Вот код:Контролируемый GenServer не перезапускается?

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 

Из приведенного выше результата мне не очень понятно, что произойдет. Похоже, что GenServer перезапущен, на основе сообщения, отображаемого в IO, но почему исключение снова выбрасывается? Кроме того, в этом коде:

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

Если вызов метода MayRaiseGenServer.maybe_will_raise действительно вызовет ошибку, это выглядит как линии после, один с timer.sleep и IO.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, который позволит мне справиться с этим, создав ошибку и продолжая выполнение? Я предполагаю, что супервизоры не будут автоматически повторять часть кода при перезапуске.

+1

Ну, в соответствии с вывода сервер был перезапущен, так как есть 2 строки с 'запущенным MyServer'. Вы также можете проверить это, вызвав: 'Process.alive? Process.whereis (MayRaiseGenServer) '. Если ваш сервер работает, он вернет true. – mentels

+0

Я заметил разные PID, но не должен ли «IO.puts» запускать выход MyServer? »? – Geo

ответ

4

Как моя точка зрения.

Ваш выход выше говорит вам трассировки стека, когда исключение был поднят с сигналом выхода в GenServer.call(MayRaiseGenServer, :maybe_will_raise, 5000) и журнал ошибок, потому что terminate/2 вызывается с причиной {%RuntimeError{message: ...}, [...].

Вы можете определить terminate/2 обратного вызова, чтобы увидеть:

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

Terminate/2

Если причина не: нормальный,: выключение ни {: выключение, термин} об ошибке регистрируется.

Но когда исключение было поднято внутри обратного вызова GenServer (кроме init/1) будет вызывать terminate/2, что говорит сервер собирается выйти (сигнал Выхода был отправлен).

Так код после этой строки не будет выполняться:

try do 
IO.inspect MayRaiseGenServer.maybe_will_raise 
... 

но не в IO.puts «начал MYSERVER» выход снова появляются?

А также, когда выйдет ваш GenServer.Ваш руководитель начинает новый связывающую основной процесс с процессом GenServer (Ваш MayRaiseGenServer.start_link получил снова позвонить)

Последняя вещь, если вы хотите, чтобы сделать код продолжить executing.You может поймать выхода сигнала как это:

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 callback.Hope raise что поможет!

3

Проблема в том, что ваш диспетчер не связан с GenServer. Поэтому он не уведомляется о смерти ребенка (через сигнал выхода). Чтобы исправить это, вам необходимо использовать GenServer.start_link/3.

Для получения дополнительной информации проверьте либо Erlang, либо Elixir документацию по процессам.

+0

Я обновил описание. Похоже, я получаю такое же поведение при использовании 'start_link' – Geo

0

По ошибке я использовал вместо GenServer.start_link и искал ответ в течение двух дней.

Примечание 1: Supervisor дерево :observer.start помогло отладить эту проблему

Примечание 2: Но выше проблема при использовании start_link только. Я пишу для людей, которые совершают ту же ошибку, что и я: -P.

0

Сервер перезапускается. Чтобы это доказать, я добавил этот фрагмент

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([]) 

Попробуйте и посмотрите