aprendiendo ( Erlang ).

lunes, 1 de agosto de 2011

Comunicación entre procesos II. Procesos en distintos nodos.

| 0 comentarios |

Con lo que ya sabemos, estamos en disposición de realizar comunicaciones más complejas entre nodos Erlang remotos. El escenario es el siguiente: tenemos dos procesos en distintos nodos (o máquinas) y necesitamos que interactúen.
Para ilustrar la comunicación de procesos, entre distintos nodos Erlang, vamos a reescribir el manido ejemplo del Ping-Pong. Ahora, el proceso pong, actuará como servidor en un nodo y, el proceso ping, como cliente realizando peticiones, en otros nodos (o el mismo). Pero, ... te preguntarás, ¿cómo sabe el cliente el PID del proceso servidor pong? La respuesta es sencilla, no lo sabe. He aquí, donde entra en juego el registro de procesos que ya vimos.
Empecemos a ver el código del proceso servidor pong, al cual he llamado originalmente pongserver:
-module(pongserver).
-export([start/0, loop/0]).

start() ->
    register (pong_server, spawn ( fun loop/0 )).

loop() ->
    receive
        {ping, Origen, Nombre, I, Max} ->
            io:format("Pong a ~s (~p/~p) - ~p ~n", [Nombre, I, Max, Origen]),
            Origen ! {pong, I},
            loop();
        terminar ->
            io:format("terminar");
        Otracosa ->
            io:format("Peticion incorrecta: ~p~n", [Otracosa]),
            loop()
    end.
Como podemos ver el código del proceso pong difiere un poco con el que ya escribimos en su momento. Hemos registrado el proceso en la función start/0 con el átomo pong_server para poder identificar el proceso servidor en cualquier otro nodo. El mensaje ping que recibe ahora tiene el formato {ping, Origen, Nombre, I, Max} donde: Origen es el PID del proceso que solicita el ping; Nombre es un nombre dado al proceso; I el número de ping solicitado y Max el número máximo de ping's a solicitar.
Sigamos con el código del proceso cliente ping, al cual he llamado originalmente pingclient (si es que dando nombres... soy la leche):
-module(pingclient).

-export([start/0, start/3, ping/3]).

start() ->
    start(node(), 3, 3).

start(Nodo, Max, NumPing) -> 
    F = fun(I) -> 
                Ping = spawn(pingclient, ping, ["PING" ++ [47+I], Max, Nodo]),
                Ping ! iniciar
        end,
    lists:foreach ( F, lists:seq(1, NumPing) ).

ping(Nombre, Max, Nodo) ->
    receive
        iniciar ->
            io:format("~s (~p/~p) ~n", [Nombre, 1, Max]),
            {pong_server, Nodo} ! {ping, self(), Nombre, 1, Max},
            ping(Nombre, Max, Nodo);
        {pong, Indice} when Indice < Max ->
            io:format("~s (~p/~p) ~n", [Nombre, Indice+1, Max]),
            {pong_server, Nodo} ! {ping, self(), Nombre, Indice+1, Max},
            ping(Nombre, Max, Nodo);
        _ ->
            io:format("Ping Terminar ~s~n", [Nombre])
    end.
Pero..., ¿qué diferencia hay a parte de utilizar más parámetros para los mensajes? Pues la verdad, en esencia es el mismo código salvo por el envío de mensajes. Hasta ahora para enviar mensajes a otros procesos utilizábamos la fórmula PID!mensaje pero ahora hemos utilizado {nombre_registrado, nodo}!mensaje. Con esta segunda forma podemos enviar un mensaje a un proceso registrado en cualquier nodo sin necesidad de conocer su PID.
$ erl -name nodoPONG@maquina -setcookie 1234
$ erl -name nodoPING@maquina -setcookie 1234


(nodoPONG@maquina)1> c(pongserver).
{ok,pongserver}
(nodoPONG@maquina)2> pongserver:start().
true
Pong a PING0 (1/3) - <8910.55.0> 
Pong a PING1 (1/3) - <8910.56.0> 
Pong a PING2 (1/3) - <8910.57.0> 
Pong a PING0 (2/3) - <8910.55.0> 
Pong a PING1 (2/3) - <8910.56.0> 
Pong a PING2 (2/3) - <8910.57.0> 
Pong a PING0 (3/3) - <8910.55.0> 
Pong a PING1 (3/3) - <8910.56.0> 
Pong a PING2 (3/3) - <8910.57.0> 




(nodoPING@maquina)1> c(pingclient).
{ok,pingclient}
(nodoPING@maquina)2> pingclient:start(nodoPONG@maquina, 3,3).
PING0 (1/3) 
PING1 (1/3) 
PING2 (1/3) 
ok
PING0 (2/3)                
PING1 (2/3)                
PING2 (2/3)                
PING0 (3/3)                
PING1 (3/3)                
PING2 (3/3)                
Ping Terminar PING0        
Ping Terminar PING1        
Ping Terminar PING2 

















Como puedes comprobar hemos abierto dos nodos uno para ping y otro para pong. Primero, hemos compilado y ejecutado el proceso servidor en el nodoPONG y posteriormente hemos hecho lo propio en el nodoPING. En los cuadros anteriores se puede comprobar fácilmente como a cada ping enviado por el cliente le corresponde una respuesta en el lado del servidor.

Pero... ¿qué es lo que esta realmente pasando?

Si la curiosidad te puede, y a mí personalmente me puede. Te resultará extraño o curioso que para comunicarse el cliente con el servidor necesites identificar el proceso y el nodo, y sin embargo, el servidor únicamente necesite el PID del cliente. Para responder a esta pregunta veamos primero el resultado de ejecutar el cliente y el servidor en el mismo nodo.
PING0 (1/3) 
PING1 (1/3) 
PING2 (1/3) 
ok
Pong a PING0 (1/3) - <0.175.0> 
Pong a PING1 (1/3) - <0.176.0> 
PING0 (2/3)              
Pong a PING2 (1/3) - <0.177.0> 
PING1 (2/3)              
Pong a PING0 (2/3) - <0.175.0> 
PING2 (2/3)              
Pong a PING1 (2/3) - <0.176.0> 
PING0 (3/3)              
Pong a PING2 (2/3) - <0.177.0> 
PING1 (3/3)              
Pong a PING0 (3/3) - <0.175.0> 
PING2 (3/3)              
Pong a PING1 (3/3) - <0.176.0> 
Ping Terminar PING0      
Pong a PING2 (3/3) - <0.177.0> 
Ping Terminar PING1      
Ping Terminar PING2  
Salta a la vista que los PID's, en ambas ejecuciones, son ligeramente distintos. En local, todos los PID's empiezan en 0 y en remoto no. Googleando y buscando y ... y ... averigüé que el primer dígito de un PID se corresponde con un identificador de nodo. El identificador de nodo 0 se utiliza para comunicaciones internas del propio nodo.
Conclusión, es por lo explicado que el servidor sólo necesite el PID del proceso cliente, ya que él lleva incorporado el nodo del cual proviene. La ventaja de todo esto es que, no van a coexistir dos PID's idénticos en el mismo nodo Erlang.

Muy listos estos chicos de Erlang, ¿NO?

Publicar un comentario

0 comentarios:

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