2010-05-05 32 views
8

Je travaille sur la documentation d'Erlang, essayant de comprendre les bases de la configuration d'un OTP gen_server et d'un superviseur. Chaque fois que gen_server se bloque, mon superviseur se bloque également. En fait, chaque fois que j'ai une erreur sur la ligne de commande, mon superviseur se bloque.erlang OTP Supervisor plantant

Je m'attends à ce que gen_server soit redémarré lorsqu'il tombe en panne. Je m'attends à ce que les erreurs de ligne de commande aient aucun relèvement sur mes composants serveur. Mon superviseur ne devrait pas s'écraser du tout.

Le code sur lequel je travaille est un "serveur d'écho" de base qui répond à tout ce que vous envoyez, et un superviseur qui redémarre le serveur echo 5 fois par minute au maximum (one_for_one). Mon code:

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

en utilisant erlc *.erl Compilé et est ici une course exemple:

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> 

Répondre

15

Le superviseur de test de problème à partir du shell est que le processus superviseur est lié au processus shell. Lorsque le processus gen_server se bloque, le signal de sortie est propagé au shell qui tombe en panne et redémarre.

Pour éviter le problème d'ajouter quelque chose comme ceci au superviseur:

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

J'ajouterais que c'est seulement une approche valide lors du développement ou du débogage. Dans un système de production en direct, il est préférable d'essayer d'emballer votre code dans une application OTP standard sous un arbre de supervision normal. –

+0

Avait exactement le même problème, unlink (Pid) fonctionne. – pranjal

+0

Jetez un coup d'oeil ici http://stackoverflow.com/questions/6720472/erlang-and-process-flagtrap-exit-true Je n'ai pas écrit de code erlang depuis quelque temps maintenant, mais si je me souviens bien, ce qui se passe est Lorsque le serveur gen_server tombe en panne, le signal de sortie est propagé à tous les processus liés. Vous pouvez intercepter le signal de sortie comme dans le lien ci-dessus, mais vous ne voulez presque jamais le faire. C'est erlang laisser la philosophie crash. – filippo

9

I wou Je vous suggère de déboguer/trace votre application pour vérifier ce qui se passe. C'est très utile pour comprendre comment les choses fonctionnent dans OTP.

Dans votre cas, vous pouvez procéder comme suit.

Démarrer le traceur:

dbg:tracer(). 

Trace toutes les fonctions appelle à votre superviseur et votre gen_server:

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

Vérifiez quels messages les processus passent:

dbg:p(new, m). 

Voir ce qui est passe à vos processus (accident, etc):

dbg:p(new, p). 

Pour plus d'informations sur le traçage:

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

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

Espérons que cela peut aider dans des situations présentes et futures.

TRUC: Le comportement gen_server attend le rappel fin/2 à définir et exportés;)

MISE À JOUR: Après la définition de la fin/2 la raison de l'accident est évident de la trace. Voici à quoi cela ressemble:

Nous (75) appelons la fonction crash/0. Ceci est reçu par le serveur gen (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, problème sur l'appel de poignée. Nous avons un BadMatch ...

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

La fonction fin est appelée. Le serveur se ferme et il n'est plus enregistré.

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

Le superviseur (77) reçoivent le signal de sortie du gen_server et il fait son travail:

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

Eh bien, il essaie ... Comme il arrive ce que Filippo a dit ...

+0

merci pour les conseils de débogage. L'erreur "function not defined ... terminate" m'a aussi troublé. Le comportement de gen_server * ne devrait pas * s'attendre à ce que terminate soit défini, puisque echo_server ne piège pas les exits. C'est par les docs, de toute façon; Je n'ai pas encore lu le code d'OTP. – drfloob

+0

Eh bien, en définissant et exportant le terminate/2 va supprimer le UNDEF, montrant la véritable raison de l'accident (badmatch sur 2). Le truc de liaison est une autre histoire ... Tu m'as un peu embrouillé maintenant. Que voulez-vous dire exactement par "le comportement de gen_server ne devrait pas s'attendre à ce que terminate soit défini, puisque echo_server ne piège pas les sorties"? –

+0

J'ai mis à jour la réponse. Regarde. –

1

D'autre part, le cas échéant redémarrage stratégie doit être testé à partir de la console, la console d'utilisation pour démarrer le superviseur et le contrôle avec pman pour tuer le processus.

Vous verriez que pman s'actualise avec le même superviseur Pid mais avec des Pid de travail différents en fonction des MaxR et MaxT que vous avez définis dans restart-strategy.