2010-05-05 4 views
8

Я работаю над документацией Erlang, пытаясь понять основы создания OTP gen_server и супервизора. Всякий раз, когда мой gen_server падает, мой диспетчер также падает. На самом деле, всякий раз, когда у меня появляется ошибка в командной строке, мой диспетчер аварийно завершает работу.erlang OTP Supervisor crashing

Ожидается, что gen_server будет перезагружен при его сбое. Я ожидаю, что ошибки в командной строке будут иметь никакой несущей код на моих серверных компонентах. Мой руководитель не должен рушиться вообще.

Код, с которым я работаю, является базовым «сервером эха», который отвечает тем, что вы отправляете, и супервизором, который будет перезапускать echo_server максимум 5 раз в минуту (one_for_one). Мой код:

echo_server.erl

-module(echo_server). 
-behaviour(gen_server). 

-export([start_link/0]). 
-export([echo/1, crash/0]). 
-export([init/1, handle_call/3, handle_cast/2]). 

start_link() -> 
    gen_server:start_link({local, echo_server}, echo_server, [], []). 

%% public api 
echo(Text) -> 
    gen_server:call(echo_server, {echo, Text}). 
crash() -> 
    gen_server:call(echo_server, crash).. 

%% behaviours 
init(_Args) -> 
    {ok, none}. 
handle_call(crash, _From, State) -> 
    X=1, 
    {reply, X=2, State}. 
handle_call({echo, Text}, _From, State) -> 
    {reply, Text, State}. 
handle_cast(_, State) -> 
    {noreply, State}. 

echo_sup.erl

-module(echo_sup). 
-behaviour(supervisor). 
-export([start_link/0]). 
-export([init/1]). 

start_link() -> 
    supervisor:start_link(echo_sup, []). 
init(_Args) -> 
    {ok, {{one_for_one, 5, 60}, 
     [{echo_server, {echo_server, start_link, []}, 
      permanent, brutal_kill, worker, [echo_server]}]}}. 

Составлено с использованием erlc *.erl, и вот пример работы:

Erlang R13B01 (erts-5.7.2) [source] [smp:2:2] [rq:2] [async-threads:0] [kernel-p 
oll:false] 

Eshell V5.7.2 (abort with ^G) 
1> echo_sup:start_link(). 
{ok,<0.37.0>} 
2> echo_server:echo("hi"). 
"hi" 
3> echo_server:crash(). 

=ERROR REPORT==== 5-May-2010::10:05:54 === 
** Generic server echo_server terminating 
** Last message in was crash 
** When Server state == none 
** Reason for termination == 
** {'function not exported', 
     [{echo_server,terminate, 
      [{{badmatch,2}, 
       [{echo_server,handle_call,3}, 
       {gen_server,handle_msg,5}, 
       {proc_lib,init_p_do_apply,3}]}, 
      none]}, 
     {gen_server,terminate,6}, 
     {proc_lib,init_p_do_apply,3}]} 

=ERROR REPORT==== 5-May-2010::10:05:54 === 
** Generic server <0.37.0> terminating 
** Last message in was {'EXIT',<0.35.0>, 
          {{{undef, 
           [{echo_server,terminate, 
             [{{badmatch,2}, 
             [{echo_server,handle_call,3}, 
             {gen_server,handle_msg,5}, 
             {proc_lib,init_p_do_apply,3}]}, 
             none]}, 
            {gen_server,terminate,6}, 
            {proc_lib,init_p_do_apply,3}]}, 
          {gen_server,call,[echo_server,crash]}}, 
          [{gen_server,call,2}, 
          {erl_eval,do_apply,5}, 
          {shell,exprs,6}, 
          {shell,eval_exprs,6}, 
          {shell,eval_loop,3}]}} 
** When Server state == {state, 
          {<0.37.0>,echo_sup}, 
          one_for_one, 
          [{child,<0.41.0>,echo_server, 
           {echo_server,start_link,[]}, 
           permanent,brutal_kill,worker, 
           [echo_server]}], 
          {dict,0,16,16,8,80,48, 
           {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[], 
           []}, 
           {{[],[],[],[],[],[],[],[],[],[],[],[],[],[], 
            [],[]}}}, 
          5,60, 
          [{1273,79154,701110}], 
          echo_sup,[]} 
** Reason for termination == 
** {{{undef,[{echo_server,terminate, 
          [{{badmatch,2}, 
          [{echo_server,handle_call,3}, 
          {gen_server,handle_msg,5}, 
          {proc_lib,init_p_do_apply,3}]}, 
          none]}, 
      {gen_server,terminate,6}, 
      {proc_lib,init_p_do_apply,3}]}, 
    {gen_server,call,[echo_server,crash]}}, 
    [{gen_server,call,2}, 
    {erl_eval,do_apply,5}, 
    {shell,exprs,6}, 
    {shell,eval_exprs,6}, 
    {shell,eval_loop,3}]} 
** exception exit: {{undef, 
         [{echo_server,terminate, 
          [{{badmatch,2}, 
           [{echo_server,handle_call,3}, 
           {gen_server,handle_msg,5}, 
           {proc_lib,init_p_do_apply,3}]}, 
           none]}, 
         {gen_server,terminate,6}, 
         {proc_lib,init_p_do_apply,3}]}, 
        {gen_server,call,[echo_server,crash]}} 
    in function gen_server:call/2 
4> echo_server:echo("hi"). 
** exception exit: {noproc,{gen_server,call,[echo_server,{echo,"hi"}]}} 
    in function gen_server:call/2 
5> 

ответ

15

Контроллеры тестирования проблем из оболочки - это то, что процесс диспетчера связан с процессом оболочки. Когда процесс gen_server выходит из строя, выходной сигнал распространяется до оболочки, которая сбой и перезапуск.

Чтобы избежать этой проблемы добавить что-то вроде этого к диспетчеру:

start_in_shell_for_testing() -> 
    {ok, Pid} = supervisor:start_link(echo_sup, []), 
    unlink(Pid). 
+5

Я бы добавил, что это только действительный подход при разработке или отладке. В живой системе производства лучше попробовать и обернуть свой код в стандартном приложении OTP под обычным деревом наблюдения. –

+0

Имел точно подобную проблему, unlink (Pid) работает. – pranjal

+0

Посмотрите здесь http://stackoverflow.com/questions/6720472/erlang-and-process-flagtrap-exit-true Я не писал код erlang в течение некоторого времени, но если я хорошо помню, что происходит, что при сбое gen_server выходной сигнал распространяется на все связанные процессы. Вы можете захватить сигнал выхода, как в ссылке выше, но вы почти никогда не хотите этого делать. Это эрланг, пусть он разрушает философию. – filippo

9

I Wou ld предлагает вам отладка/трассировка ваше приложение, чтобы проверить, что происходит. Это очень полезно для понимания того, как все работает в OTP.

В вашем случае вы можете сделать следующее.

Начало Tracer:

dbg:tracer(). 

Трассировка всех вызовов функций для вашего руководителя и ваш gen_server:

dbg:p(all,c). 
dbg:tpl(echo_server, x). 
dbg:tpl(echo_sup, x). 

Проверьте, какие сообщения проходят процессы:

dbg:p(new, m). 

Посмотрите, что происходит с вашими процессами (авария и т. д.):

dbg:p(new, p). 

Для получения дополнительной информации об отслеживании:

http://www.erlang.org/doc/man/dbg.html

http://aloiroberto.wordpress.com/2009/02/23/tracing-erlang-functions/

Надеется, что это может помочь в этом и будущих ситуациях.

ПОЯСНЕНИЯ: Поведение gen_server ожидает обратного вызова прекращается/2, которые будут определены и экспортированы;)

ОБНОВЛЕНИЕ: После того, как определение прекратить/2 причиной аварии очевидна от следа. Вот как это выглядит:

Мы (75) вызываем функцию crash/0. Это принято gen_server (78).

(<0.75.0>) call echo_server:crash() 
(<0.75.0>) <0.78.0> ! {'$gen_call',{<0.75.0>,#Ref<0.0.0.358>},crash} 
(<0.78.0>) << {'$gen_call',{<0.75.0>,#Ref<0.0.0.358>},crash} 
(<0.78.0>) call echo_server:handle_call(crash,{<0.75.0>,#Ref<0.0.0.358>},none) 

Ух, проблема при вызове ручки. У нас есть badmatch ...

(<0.78.0>) exception_from {echo_server,handle_call,3} {error,{badmatch,2}} 

Функция прекращает называется. Сервер выходит из системы, и он становится незарегистрированным.

(<0.78.0>) call echo_server:terminate({{badmatch,2}, 
[{echo_server,handle_call,3}, 
    {gen_server,handle_msg,5}, 
    {proc_lib,init_p_do_apply,3}]},none) 
(<0.78.0>) returned from echo_server:terminate/2 -> ok 
(<0.78.0>) exit {{badmatch,2}, 
[{echo_server,handle_call,3}, 
    {gen_server,handle_msg,5}, 
    {proc_lib,init_p_do_apply,3}]} 
(<0.78.0>) unregister echo_server 

Супервайзер (77) получить сигнал на выход из gen_server и он делает свою работу:

(<0.77.0>) << {'EXIT',<0.78.0>, 
         {{badmatch,2}, 
         [{echo_server,handle_call,3}, 
         {gen_server,handle_msg,5}, 
         {proc_lib,init_p_do_apply,3}]}} 
(<0.77.0>) getting_unlinked <0.78.0> 
(<0.75.0>) << {'DOWN',#Ref<0.0.0.358>,process,<0.78.0>, 
         {{badmatch,2}, 
         [{echo_server,handle_call,3}, 
         {gen_server,handle_msg,5}, 
         {proc_lib,init_p_do_apply,3}]}} 
(<0.77.0>) call echo_server:start_link() 

Ну, он пытается ... Так как это происходит, что сказал Филиппо ...

+0

спасибо за подсказки отладки. Ошибка «function not defined ... terminate» меня тоже путала. Поведение gen_server * не должно * ожидать окончания завершения, поскольку echo_server не захватывает выходы. Во всяком случае, это за документы. Я еще не читал код OTP. – drfloob

+0

Ну, определяя и экспортируя terminate/2, мы удалим UNDEF, показывая реальную причину сбоя (badmatch on 2). Связующее вещество - это еще одна история ... Ты немного смутил меня. Что вы имеете в виду именно с «поведение gen_server, не следует ожидать, что конец будет определен, поскольку echo_server не улавливает выходы»? –

+0

Я обновил ответ. Взгляни. –

1

С другой стороны, если на все рестарт-стратегии должно быть проверено внутри консоли, используйте консоль, чтобы запустить супервизор и проверку с pman, чтобы убить процесс.

Вы увидите, что pman обновляется с тем же супервизором Pid, ​​но с разными рабочими Pids в зависимости от MaxR и MaxT, которые вы установили в стратегии перезапуска.

Смежные вопросы