aprendiendo ( Erlang ).

sábado, 1 de septiembre de 2012

Monitor de procesos. It's alive.

| 0 comentarios |

Ya vimos en su momento como realizar un control de errores en sistemas concurrentes. Es decir, el uso de link's y de trap_exit. Los procesos enlazados nos dota de un mecanismo por el cual, dos procesos A y B quedaban enlazados de tal forma que, si el proceso A moría enviaba una señal exit al proceso B y viceversa. Con dicho mecanismo evitábamos que hubieran procesos moribundo creados en nuestro sistema. Muere rápido para recuperarte lo antes posible.

Puede que no deseemos un control tan férreo como nos propone el sistema de procesos enlazados. A lo mejor, sólo necesitamos saber si esta vivo, o no, el proceso A para poder utilizarlo. A lo mejor, no nos supone ningún problema seguir trabajando sin el proceso A. En estos casos, lo que necesitaremos es un monitor. Un monitor es un tipo de enlace asimétrico, mientras que los enlaces (link's) son de carácter simétrico. Es decir, si el proceso M monitoriza al proceso A, el proceso M sabrá sobre el proceso A, pero no al revés, como pasa con los procesos enlazados.

Un ejemplo que ilustre claramente este comportamiento es el caso de servidores de apoyo. Su ponte que disponemos de un servidor central y otro de apoyo. El servidor central un día decide caerse. ¿No estaría bien que los clientes pudieran monitorizar al servidor? Es lo suyo, verdad. Para que en el caso de caídas del servidor central pasar automáticamente al servidor de respaldo. Un comportamiento muy deseable en muchos casos.

Pero para ilustrar el comportamiento de los monitores he decidido utilizar un ejemplo más modesto.

El paciente.

Todos hemos pasado por la desagradable experiencia de visitar una unidad de cuidados intensivos. Los pacientes en estos lugares están enganchados a unos monitores, o mejor dicho, los monitores están enganchados a los pacientes. Pues este, es el modelo elegido para ilustrar el uso de monitores. Empecemos por la implementación del paciente.

-module(paciente).
-export([start/0, reanimar/0, muerto/0, muerte_natural/0, asfixia/0, paro_cardiaco/0, mensaje/1]).

start () ->
    register(?MODULE, spawn(fun proceso/0)).

reanimar () ->
    start().

proceso() ->
    receive
        muerto ->
            io:format ("El paciente ha muerto. Hora de la muerte ~p ~n", [erlang:localtime()]),
            exit(normal);
        {alarma, Motivo} ->
            io:format ("Monitorizado alarma ~p~n", [Motivo]),
            exit(Motivo);
        Otro ->
            io:format("Recibido <~p> ~n", [Otro]), 
            proceso()
    end.

muerto() ->
    ?MODULE!muerto.

muerte_natural() ->
    ?MODULE!{alarma,muerte_natural}.

asfixia() ->
    ?MODULE!{alarma,asfixia}.

paro_cardiaco() ->
    ?MODULE!{alarma,paro_cardiaco}.

mensaje(M) ->
    ?MODULE!M.

Se trata de un proceso simple que puede morir o elevar una alarma. Como no dispongo de ningún paciente cerca para poder poner sensores, he decidido crear un interfaz que nos permita hacer que el paciente sufra alguna crisis. Hasta el momento nada nuevo bajo el sol.

Monitor

Ahora vamos con el monitor. El monitor se encarga de avisar, en caso de algún tipo de alarma o crisis del paciente. El enfermero, doctor o cualquier personal sanitario será el encargado de reiniciar el sistema en caso de recuperación por parte del paciente.

-module(monitor).

-compile(export_all).

start(Pid) ->
    spawn(?MODULE, monitor, [Pid]).

monitor(Pid) ->
    io:format("Monitorizando al paciente ~p. Constantes estables.~n", [Pid]),
    erlang:monitor(process,Pid),
    receive
        {'DOWN', Ref, process, Pid,  normal} ->
            io:format("~p dice que  ~p ha muerto por causas naturales~n",[Ref,Pid]);
        {'DOWN', Ref, process, Pid,  Reason} ->
            io:format("~p dice que  ~p ha muerto por ~p~n",[Ref,Pid,Reason]);
        Otro ->
            io:format("Monitor recibe <~p>~n", [Otro])
    end.

La función start/1 se encarga iniciar el monitor para el paciente que tenga un Pid dado.

Como puede observar, la función que se encarga de indicar al sistema que cree un monitor para un proceso (paciente) es erlang:monitor/2 (documentación). Inmediatamente después pasamos a esperar las posibles incidencias del paciente. El mensaje que podemos recibir tiene el formato {'DOWN', RefMonitor, process, PidMonitorizado, Motivo}.

1> c(paciente).
{ok,paciente}
2> c(monitor).
{ok,monitor}
3> paciente:start().
true
4> monitor:start(whereis(paciente)).
Monitorizando al paciente <0.44.0>. Constantes estables.
<0.46.0>
5> paciente:mensaje(hola).
Recibido <hola> 
hola
6> paciente:muerto().
El paciente ha muerto. Hora de la muerte {{2012,8,9},{19,2,37}} 
muerto
#Ref<0.0.0.109> dice que  <0.44.0> ha muerto por causas naturales

La implementación es sencilla, y el concepto de monitor es cuanto menos valioso. Pero este monitor es la gama más baja de este tipo de cacharros. Así que, un día el hospital decide renovar sus viejos monitores y compra un monitor-reanimador.

Reanimador.

La única diferencia con su predecesor es que le hemos dotado de la posibilidad de reanimar al paciente. En el caso de riesgo.

-module(reanimador).

-compile(export_all).

start() ->
    paciente:start(),
    spawn(?MODULE, monitor, [whereis(paciente)]).

monitor(Pid) ->
    io:format("Monitorizando al paciente ~p. Constantes estables.~n", [Pid]),
    erlang:monitor(process,Pid),
    receive
        {'DOWN', Ref, process, Pid,  normal} ->
            io:format("~p dice que  ~p ha fallecido~n",[Ref,Pid]);
        {'DOWN', Ref, process, Pid,  Reason} ->
            io:format("~p: Alarma. El paciente ~p esta padeciendo un/a  ~p~n",[Ref,Pid,Reason]),
            io:format("Reanimar al paciente. Vuelve ... vuelve ... vive~n"),
            paciente:reanimar (),
            monitor(whereis(paciente))
    end.

veamos como actúa el reanimador.

7> c(reanimador).
{ok,reanimador}
8> reanimador:start().
Monitorizando al paciente <0.39.0>. Constantes estables.
<0.40.0>
9> paciente:asfixia().
Monitorizado alarma asfixia
{alarma,asfixia}
#Ref<0.0.0.63>: Alarma. El paciente <0.39.0> esta padeciendo un/a  asfixia
Reanimar al paciente. Vuelve ... vuelve ... vive
Monitorizando al paciente <0.43.0>. Constantes estables.
10> paciente:paro_cardiaco().
Monitorizado alarma paro_cardiaco
{alarma,paro_cardiaco}
#Ref<0.0.0.72>: Alarma. El paciente <0.43.0> esta padeciendo un/a  paro_cardiaco
Reanimar al paciente. Vuelve ... vuelve ... vive
Monitorizando al paciente <0.45.0>. Constantes estables.
11> paciente:muerto().
El paciente ha muerto. Hora de la muerte {{2012,8,9},{19,46,22}} 
muerto
#Ref<0.0.0.80> dice que  <0.45.0> ha fallecido

Ahora, resuelta más difícil que el paciente fallezca. Otra propiedad que hasta ahora, no había mencionado es la posibilidad de apilar los monitores.

Publicar un comentario en la entrada

0 comentarios:

 
Licencia Creative Commons
Aprendiendo Erlang por Verdi se encuentra bajo una Licencia Creative Commons Atribución-NoComercial-CompartirIgual 3.0 Unported.