Anillo de Mensajes en Erlang

diciembre 24, 2010

Desde hace algún tiempo, llevo investigando el mundo de la programación funcional. Después de probar varios lenguajes como F#, o Clojure, finalmente me decanté por Erlang.

La verdad es que estoy pasandomelo bien recordando los viejos tiempos de Lisp o Prolog :), y sobre todo aprendiendo una forma diferente de afrontar los problemas.

Para el aprendizaje, mi libro de cabecera es Programming Erlang de la editorial Pragmatic Bookshelf. Libro que me esta gustando bastante, ya que asume que no tienes ni idea de programación funcional, como era mi caso.

Quizás para otro post os pueda contar algo de los beneficios que tiene la programación funcional, como por ejemplo, lo bien que escalan en entornos multiprocesador y lo robustos que son para entornos multihilo, gracias a la eliminación de los «side-effects».

En dicho libro se plantea un ejercio en el que se pide que se cree un «anillo» de M procesos, de tal manera que enviando un mensaje a uno de esos procesos, dicho mensaje circule por el anillo N veces.

Una vez que se tiene la estructura, se pide que se mida el tiempo que tarda en circular los M*N mensajes, se compare con lo mismo hecho en otro lenguaje de programación (preferiblemente no funcional) y después que se publique en un blog.

Así que obediente de mi, aqui os dejo la primera parte, el ejercicio hecho en Erlang. Para otra entrada dejo la comparación con lo mismo hecho en otro lenguaje 🙂

-module(ring).
-export([ringtest/2, ringNode/1]).

ringNode(PPid) ->
	receive
		{exit} ->
			PPid!{exit},
			true;
		{0} ->
			PPid!{exit},
			true;
		{N} ->
			PPid!{(N-1)},
			ringNode(PPid)
	end.

ringtest(N, M) ->
	ringtest(N-2, self(), N*(M-1)).

ringtest(0, ThisPid, M) ->
	Pid = spawn(ring, ringNode, [ThisPid]),
	Pid!{M},
	ringNode(Pid);
ringtest(N, ThisPid, M) ->
	Pid = spawn(ring, ringNode, [ThisPid]),
	ringtest(N-1, Pid, M).

Analizando los tiempos (que están en microsegundos), lo cierto es que erlang es bastante rápido, tanto en crear procesos como en enviar mensajes entre ellos. El ordenador que ejecuto el programa era un ATOM D525 de 1.8Ghz y en crear 5000 procesos, y enviar el mensaje 100 veces alrededor del anillo, sólo tardó 1,2 segundos. Sin duda, una cifra bastante interesante.

Despueés de ver estos números, esta claro que no es casulidad que sea una de las plataformas preferidas a la hora de desarrollar aplicaciones que se muevan en estos tipos de entornos.

No soy ningun experto de Erlang, de hecho aun estoy aprendiendo, así que alguien lo lee y tiene algún comentario, serán bienvenidos!


Objetos distribuidos en Cocoa usando NSConnection y NSProxy

diciembre 11, 2010

Cuando nuestros programas crecen lo suficiente, nos encontramos con situaciones donde tenemos varios hilos de ejecución o incluso diferentes procesos que se ejecutan de forma paralela en la misma o en diferente máquina. En estas situaciones, es habitual tener que comunicar estos diferentes módulos.

Una típica forma de comunicación es el uso de objetos «distribuidos», desde los vetustos (y terroríficos :)) objetos CORBA, pasando por RMI de Java o Remoting de .NET, hasta los mas modernos WebServices, estos mecanismos han sido y son ampliamente utilizados.

Explicado de una forma muy simple, estos mecanismos permiten llamar a un método de una instancia de un objeto que no se esta ejecutando en el mismo entorno que el llamador. Ese entorno puede ser otro hilo, otro proceso u otra máquina.

Como era de esperar, Cocoa plantea su aproximación al uso de objetos distribuidos. Como hemos hablado anteriormente en el blog, el mecanismo de invocación de métodos en Objetive-c basado en mensajes, ayuda a entender y a enfocar de otra manera el funcionamiento de la invocación remota de métodos de objetos.

En un lenguaje como Java, cuando invocas a un método remoto, realmente estas invocando a un método que tiene que existir en una clase «local». Esta clase «local» habitualmente es un Proxy que se autogenera estáticamente. El Proxy por su parte, transforma la llamada en una comunicación con el servidor remoto, que se encarga de invocar el método y devolver el retorno.

En Objetive-C, una llamada a un objeto remoto, se traduce en un mensaje que se «enruta» hasta llegar al objeto receptor. En este caso, el Proxy no es necesario autogenerarlo, pues como hablamos, el Proxy será un objeto que es capaz de recibir en tiempo de ejecución cualquier tipo de mensaje. No tenemos la necesidad de que el método realmente exista para poder compilar nuestro código.

Las clases que entran en juego son numerosas, y el funcionamiento completo tiene su «intríngulis» :), por lo que en este post nos centraremos en el aspecto mas práctico, es decir, poner un ejemplo de como funciona.

El código se divide en dos partes, por un lado esta el objeto que va e estar ejecutandose en modo «servidor» y por otro, el lado del cliente.

Hablando del servidor, vamos a tener un objeto de tipo «Calculadora» que va a tener un método «addNumber:with:», que como dice su nombre, sumará dos números. A este objeto lo vamos a llamar de forma remota.

@protocol CalculatorProtocol
- (int) addNumber:(int) a with:(int)b;
@end

@interface Calculator : NSObject<CalculatorProtocol> {
}
@end

El protocolo, nos va a ayudar a que el proxy sepa que el método existe. Realmente esto no es estrictamente necesario, ya que el mensaje se enrutará de todas maneras hasta llegar a la clase Calculator, pero sin embargo es una buena ayuda de cara a la comprobación que el runtime hace para saber si la clase remota responde o no al mensaje.

Una vez que tenemos el objeto que queremos exportar, necesitamos publicarlo, o como se llama en la documentación: «Vending an Object».

Para ello, vamos a hacer uso de la clase NSConnection

Calculator* calculator = [[Calculator alloc] init];
NSConnection* theConnection = [[[NSConnection alloc] init] autorelease];
[theConnection setRootObject:calculator];
[theConnection registerName:@"calculator"];

[[NSRunLoop currentRunLoop] run]; 
 // Solo si nuestra aplicación no lo ha hecho ya
 // En caso de una aplicación Cocoa, esto no es necesario

Con esas 5 lineas, nuestro Servidor ya estará escuchando peticiones, y el objeto estará listo para usarse desde otro entorno de ejecución, fácil, ¿no?.

La parte cliente, no es que sea mucho mas compleja 😉

id theProxy;
theProxy = [[NSConnection rootProxyForConnectionWithRegisteredName:@"calculator" host:nil] retain];
[theProxy setProtocolForProxy:@protocol(CalculatorProtocol)];
NSLog(@"%d", [theProxy addNumber:5 with:3]);

Una vez mas el objeto NSConnection nos va a servir para obtener la conexión al objeto remoto. Usamos la misma cadena que se utilizó en el registro, y pasamos «nil» al parámetro host, ya que en este ejemplo, el servidor esta en la misma máquina.

Ambos fragmentos de código se ejecutan en procesos diferentes, y como se espera, el resultado del cliente es imprimir un 8 en la pantalla de logs.

Sin duda, el post no ataca toda la complejidad que hay detrás de los mecanismos, y las diferentes alternativas que existen de cara a la configuración de la conexión u otros aspectos que entran en juego, pero aún así se puede ver lo sencillo que es invocar un mensaje sobre un objeto que esta ejecutandose fuera de nuestro proceso.