2010-05-05 13 views
8

Estoy trabajando con la documentación de Erlang, tratando de entender los conceptos básicos de la configuración de un OTP gen_server y supervisor. Cada vez que mi gen_server falla, mi supervisor se cuelga también. De hecho, cada vez que aparece un error en la línea de comando, mi supervisor se cuelga.erlang OTP Supervisor estrellarse

Espero que el gen_server se reinicie cuando se cuelgue. Espero que los errores de línea de comando tengan sin rumbo alguno en los componentes de mi servidor. Mi supervisor no debería estrellarse en absoluto.

El código con el que estoy trabajando es un "servidor de eco" básico que responde con todo lo que envía, y un supervisor que reiniciará echo_server 5 veces por minuto como máximo (one_for_one). Mi código:

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]}]}}. 

compilado usando erlc *.erl, y he aquí una muestra de ejecución:

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> 

Respuesta

15

El problema al evaluar a los supervisores desde el shell es que el proceso del supervisor está vinculado al proceso del shell. Cuando el proceso gen_server falla, la señal de salida se propaga al shell que se bloquea y se reinicia.

Para evitar el problema añadir algo como esto al supervisor:

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

Debo añadir que este es solo un enfoque válido al desarrollar o depurar. En un sistema de producción en vivo, es mejor intentar envolver su código en una aplicación OTP estándar bajo un árbol de supervisión normal. –

+0

Tenía exactamente un problema similar, funciona unlink (Pid). – pranjal

+0

Eche un vistazo aquí http://stackoverflow.com/questions/6720472/erlang-and-process-flagtrap-exit-true No he escrito el código erlang desde hace un tiempo, pero si recuerdo bien, lo que está sucediendo es que, cuando gen_server falla, la señal de salida se propaga a todos los procesos vinculados. Puede atrapar la señal de salida como en el enlace de arriba, pero casi nunca quiere hacerlo. Es erlang deja que se rompa la filosofía. – filippo

9

I que está Le sugiero que depurar/rastrear su aplicación para verificar qué está pasando. Es muy útil para comprender cómo funcionan las cosas en OTP.

En su caso, es posible que desee hacer lo siguiente.

Comenzar el trazador:

dbg:tracer(). 

seguimiento de todas las llamadas de función de su supervisor y gen_server:

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

Comprobar los mensajes que los procesos están pasando:

dbg:p(new, m). 

ver lo que hay sucediendo a sus procesos (bloqueo, etc.):

dbg:p(new, p). 

Para obtener más información acerca del seguimiento:

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

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

la esperanza que esto puede ayudar para esta y futuras situaciones.

PISTA: El comportamiento gen_server está esperando la devolución de llamada termina/2 que se definan y se exportan;)

ACTUALIZACIÓN: Después de la definición de la terminar/2 la razón del accidente es evidente de la huella Así es como se ve:

Nosotros (75) llamamos a la función crash/0. Esto es recibido por 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) 

Uh, problema en el manejo de la llamada. Tenemos un BadMatch ...

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

La función se llama terminar. El servidor sale y no se registra.

(<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 

El Supervisor (77) recibe la señal de salida de la gen_server y hace su trabajo:

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

Bueno, trata ... Como ocurre lo dijo Filippo ...

+0

gracias por las sugerencias de depuración. El error "función no definida ... terminar" me confundió también. El comportamiento gen_server * no debe * esperar que se defina terminate, ya que echo_server no está capturando salidas. Eso es por los documentos, de todos modos; No he leído el código de OTP todavía. – drfloob

+0

Bueno, definir y exportar el terminate/2 eliminará el UNDEF, mostrando el motivo real del bloqueo (badmatch en 2). El enlace es otra historia ... Me confundiste un poco ahora. ¿Qué quiere decir exactamente con "el comportamiento gen_server no debe esperar que se defina terminate, ya que echo_server no está capturando salidas"? –

+0

He actualizado la respuesta. Echar un vistazo. –

1

Por otro lado, en todo caso reinicio-estrategia tiene que ser probado desde el interior de la consola, el uso de la consola para iniciar el supervisor y verificación con pman para matar el proceso.

Vería que el generador de impulsos se actualiza con el mismo supervisor Pid pero con diferentes Pids de trabajador dependiendo del MaxR y MaxT que haya establecido en la estrategia de reinicio.

Cuestiones relacionadas