Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

Aplicaciones Distribuidas y Node.js

Como ya lo había anunciado, el martes pasado estuve dando una charla de aplicaciones distribuidas y Node.js, gracias a la gente de Node.js Argentina,  en el auditorio del Microsoft User Group de Argentina, en Buenos Aires.

Pueden ver y bajarse la presentación desde mi Skydrive

La idea fue mostrar algunos conceptos, y experimentos que estuve haciendo con Node.js, convirtiendo anteriores trabajos en otras tecnologías. Y mostrar código con ejemplos concretos.

Primero, expliqué lo que era una aplicación distribuida en el contexto de esta charla. Una aplicación distribuida es una aplicación que se ejecuta en varias computadoras, conectadas en red, que interactúan para resolver un problema. Ver http://en.wikipedia.org/wiki/Distributed_computing

Es decir, va más allá del clásico cliente servidor. Podemos tener varias computadoras para hacer web crawling de un sitio, o resolviendo la renderización de escenas de una película 3d. Las computadores que intervienen en el trabajo no necesariamente tienen el mismo software o programa: puede que algunas se ocupen solamente de algún paso del problema. Tampoco tienen que tener la misma plataforma o lenguaje de programación.

Lo que quise destacar, entonces, en la charla, son las facilidades que nos da Node.js y JavaScript para construir este tipo de aplicaciones. Ya en otras charlas afirmé “JavaScript es una manteca”, para destacar su flexibilidad. Y ahora, en esta charla, tuve la oportunidad de mostrar un tópico donde Node.js brilla: tanto por su soporte de red en los módulos “built-in”, como por su ecosistema, como por la filosofía de combinar módulos (como en Unix, ver Unix philosophy and Node.js de @izs)

Mencioné una entrevista a Alan Kay (ver su Dynabook)


I thought of objects being like biological cells and/or individual computers on a network, only able to communicate with messages

Until real software engineering is developed, the next best practice is to develop with a dynamic system that has extreme late binding in all aspects

The big idea is “messaging”

Es interensante que Kay mencionara células biológicas: nuestros nodos en ejecución pueden tomarse como células, que forman el organismo de nuestra aplicación distribuida. Como dice Kay, el gran tema es el “messaging”, el envío de mensajes entre las partes. Las aplicaciones distribuidas nos dan la oportunidad de olvidarnos del concepto de función/rutina a la que llamamos con parámetros y esperamos que termine de ejecutar para dar un resultado, y aún de una llamada con callback: un elemento de nuestra aplicación distribuida enviará un mensaje, desentendiéndose de obtener una respuesta.

También mencioné a Richard Feynman (algún post de mis enlaces), ver

http://boards.straightdope.com/sdmb/showthread.php?t=159936

Feynman was once asked by a Caltech faculty member to explain why spin 1/2 particles obey Fermi-Dirac statistics. He gauged his audience perfectly and said "I'll prepare a freshman lecture on it." But a few days later he returned and said, "You know, I couldn't do it. I couldn't reduce it to the freshman level. That means we really don't understand it."

Adaptando su frase a “si no lo podemos explicar, es que no lo entendemos” como motivación personal para dar charlas. Recordé su trabajo de estudio “por su cuenta” de la física cuántica según Feyman por Dyson, como justificación lejana a experimentar con un tema, en vez de tomar lo que ya está hecho directamente.

Pues lo que presenté luego, son experimentos sobre Node.js para aplicaciones distribuidas. Pero ¿por qué Node.js? En JavaScript, un mensaje puede ser un simple objeto serializable a JSON (un objeto sin ciclos, o sea, un árbol). Y para el transporte del mensaje podemos elegir multitud de módulos en el ecosistema de Node, como usar el require(‘net’) o require(‘http’) que vienen “built-it” en el núcleo de Node.js

Preguntas que tenemos que hacernos en cada caso de aplicación distribuida:

¿Qué elementos de nuestra aplicación producen mensajes?

¿Qué elementos consumen mensajes?

Un mensaje ¿es consumido por un solo elemento? ¿o puede ser procesado por varios elementos interesados?

Las máquinas y programas que ejecutan ¿están determinados desde el comienzo? ¿o pueden variar con el tiempo? Por ejemplo, me gustaría sumar a mi aplicación distribuida unas cuarenta máquinas que a la noche están ociosas. ¿Se puede hacer? ¿Necesitamos esta característica?

Un mensaje producido en un elemento de una máquina ¿se consume localmente? ¿o puede viajar a otra máquina? ¿quién decide eso? ¿el programador, el programa supervisor?

Y dado un mensaje que tiene que llegar a otra máquina ¿cómo llega? ¿con qué transporte?

Si un mensaje tiene que ser procesado remotamente ¿cómo se selecciona la máquina destino? ¿por programa? ¿por algún balanceo de carga?

Esas preguntas se contestarán diferente en cada experimento o ejemplo que tengamos.

Uno de los experimentos (repetido de distintas formas) es un web crawler, compuesto lógicamente (partes lógicas, luego a implementar de distinta forma según el ejemplo/experimento):

Cuando tengamos que recuperar las páginas de un sitio, primero enviamos una URL inicial a un elemento Resolver. Este elemento mantiene la lista de páginas ya visitadas. Si la URL es una página no procesada aún, se envía la URL a un elemento Downloader (elemento lógico que puede estar implementado en varias máquinas). Es el encargado de conseguir el contenido de la página. Luego le pasa ese dato al elemento Harvester, que extrae los enlaces que consigue encontrar en la nueva página. Los enlaces se envían al Resolver, que examina si corresponde o no procesar esas URLs.

Un módulo que escribí (no fue el primero, sino que surgió de refactor y extracción de otros casos de uso) es ObjectStream

https://github.com/ajlopez/ObjectStream
https://github.com/ajlopez/ObjectStream/tree/master/samples/broadcast

que me permite enviar un objeto JavaScript (serializable a JSON) a un Object Stream, un stream que recibe objetos. Este stream puede enviar ese objeto, ya serializado a texto, por otro stream. Y hay un Object Stream que toma esas líneas de texto y las reconstruye como objetos. Esto me permite usar, por ejemplo, un stream de socket para enviar objetos JavaScript de una máquina a otra. Vean en los enlaces de arriba el ejemplo de Broadcast que mostré en la charla.

Basado en ObjectStream, me animé luego a armar

https://github.com/ajlopez/SimpleMessages
https://github.com/ajlopez/SimpleMessages/tree/master/samples/Broadcast

Permite que dos procesos establezcan canales bidireccionales de envío de mensajes, que arrivan en cualquier momento. Desde el punto de vista del código, uno puede exponer un servidor (o varios) o crear clientes que se conectan a otros servidores. Pero luego de efectuada la conexión, el intercambio de mensajes es simétrico: cualquiera de las partes puede enviar un mensaje en cualquier momento. Ya con esto podemos entretenernos bastante, escribiendo aplicaciones distribudas basadas en mensajes.

Pero a veces, necesitamos más que enviar un simple mensaje. Por ejemplo, se nos puede presentar la necesidad de llamar a un objeto que está ejecutando en otra máquina. Podemos implementar un Remote Procedure Call. Lo hice en:

https://github.com/ajlopez/SimpleRemote

Basado en SimpleMessages, tanto el cliente como el servidor pueden exponer un objeto “hacia afuera”, y la otra parte lo puede invocar, como si fuera un objeto local. Solamente que como el retorno de la respuesta no es inmediato, el programador que llama al método agrega un callback para procesar la respuesta cuando ésta llegue.

Para alternativas pueden ver:

http://stackoverflow.com/questions/5010814/whats-the-best-way-to-make-one-node-js-server-talk-to-another
http://stackoverflow.com/questions/7986088/rpc-and-messagequeues-in-node-js
https://github.com/substack/dnode
https://github.com/Flotype/now
https://code.google.com/p/protobuf-for-node/
https://code.google.com/p/protobuf/
https://github.com/Frans-Willem/IPCNode

Vean dnode, cómo tienen clientes para distintos lenguajes. O vean también algo más agnóstico, http://en.wikipedia.org/wiki/JSON-RPC

Tuve un caso de uso donde necesitaba repartir mensajes, no solamente enviar un mensaje desde A hasta B. Entonces nació:

https://github.com/ajlopez/SimpleBroadcast
https://github.com/ajlopez/SimpleBroadcast/tree/master/samples/Broadcast

De esta manera, un cliente puede enviar un mensaje a un servidor, y éste repartirlo en los demas clientes conectados. Vean que el mensaje no se transmite al cliente que lo originó, sólo a los demás. Hasta puede haber una “estrella” de servidores, donde cada servidor atiende a sus clientes, y actúa como cliente de los otros servidores:

Esto permite la escalabilidad agregando servidores a la estrella. También tiene un concepto algo más sofisticado, el servidor repetidor (ver el código y tests). En este software tuve colaboración de Fernando Lores.

En otros casos de uso, en vez de enviar mensajes, necesité que los que iban a procesar mensajes los pidieran explícitamente. Como quería realizar todos estos experimentos en Node.js, sin depender de software externo, escribí:

https://github.com/ajlopez/SimpleQueue
https://github.com/ajlopez/SimpleQueue/tree/master/samples/DistributedProducerConsumer

Al principio, implementé una cola en memoria, local. Y luego la expuse hacia otras máquinas usando SimpleRemote.

Otro caso de uso con el que me encontré es: necesito publicar mensajes, pero no sé de antemano a quién le interesa. Es similar a los eventos de Node.js: uno emite un evento, pero no sabe quienes se suscribieron a ellos. Llevado eso a algo distribuido escribí:

https://github.com/ajlopez/SimpleBus
https://github.com/ajlopez/SimpleBus/tree/master/samples/Market

Tanto los que publican como los que se suscriben a mensajes pueden estar en máquinas distintas.

En el ejemplo de Market que mostré en la charla (ver el enlace de arriba) la suscripción a los mensajes se puede hacer dando un mensaje ejemplo: todos los mensajes que tengan las mismas propiedades que el ejemplo, serán tomados para procesar por el subscriptor. También puedo enviar un predicado, y notablemente, ese predicado puede “viajar” desde el suscriptor a la maquina que tiene el servidor de bus (que podrían ser varias máquinas en estrella usando SimpleBroadcast).

Muchos de estos casos de uso fueron motivados por implementaciones que tuve que escribir en otras tecnologías. El trabajo que más influyó es lo que hice cuando trabajé para @asehmi:

Ver Remember Fabriq.

Basado en su proyecto Fabriq, reescribí algo en AjFabriqNode:

https://github.com/ajlopez/AjFabriqNode
https://github.com/ajlopez/AjFabriqNode/tree/master/samples/WebCrawler

AjFabriq se basa en tener varios nodos ejecutándose, cada uno declara un árbol de procesadores de mensajes. En base al contenido del mensajem se determina qué procesador de mensaje puede atenderlo. Cada nodo se puede conectar a otro de la red, y entre varios se intercambian cuáles procesadores de mensajes tiene cada uno. Entonces, podemos tener un Web Crawler distribuido donde en un nodo tenemos el resolver, y en otros tenemos downloaders y harvesters.

Otros prefieren enviar los mensajes directamente a un elemento específico. Basado en la implementación de actores en Akka/Scala, ver:

http://doc.akka.io/docs/akka/snapshot/general/actor-systems.html
http://doc.akka.io/docs/akka/snapshot/general/actors.html
http://doc.akka.io/docs/akka/snapshot/general/addressing.html

implementé algo más simple:

https://github.com/ajlopez/SimpleActors
https://github.com/ajlopez/SimpleActors/tree/master/samples/DistributedWebCrawler

Pero vean que en Akka, un mensaje se envía en general a un actor, identificado por su dirección.

Hay un proyecto Java, Storm Project en:

http://storm-project.net/

donde se puede armar una topología con los elementos lógicos que van a recibir, procesar y emitir tuplas (lo que para esta charla es mensajes). Hay productores de mensajes, los Spout, y procesadores de mensajes, los Bolt. Luego cada elemento puede ejecutarse en varias máquinas. Así, podemos tener una topología de Web Crawler con Resolver, Downloader y Harvester. Pero luego poner 20 downloaders en 10 máquinas, y 5 harvesters en las mismas máquinas u otras.

Así que, ni lerdo ni perezoso, escribí algo simple en Node.js:

https://github.com/ajlopez/SimpleStorm
https://github.com/ajlopez/SimpleStorm/tree/master/samples/DistributedWebCrawler

Lo que ví en mis experimentos, es que muchas veces necesito levantar varias máquinas, indicarles qué hacer, que aplicaciones lógicas ejecutar (por ejemplo, el web crawler), y que puedan intercambiar mensajes entre ellos. Mostré:

https://github.com/ajlopez/MultiNodes
https://github.com/ajlopez/MultiNodes/tree/master/samples/collatz

Lo interesante es que un nuevo nodo que se incorpora a la red, puede ser instruido dinámicamente a ejecutar algo (como instalar y lanzar una nueva instancia del web crawler).

Dos ejemplos finales:

Una implementación de algoritmo genético distribuido:

https://github.com/ajlopez/SimpleGA
https://github.com/ajlopez/SimpleGA/tree/master/samples/tspdistr

Y un fractal que se puede calcular en el browser, o en un servidor Node.js/Express, solo o ayudado por nodos worker adicionales:

https://github.com/ajlopez/NodeSamples/tree/master/Fractal
https://github.com/ajlopez/NodeSamples/tree/master/Fractal/html
https://github.com/ajlopez/NodeSamples/tree/master/Fractal/server
https://github.com/ajlopez/NodeSamples/tree/master/Fractal/distributed

Al final, mencioné varias razones para investigar con aplicaciones distribuidas: desde lo interesante del tema, como lo práctico (para Big Data, cálculo distribuido y en paralelo, etc). Pero resalté que tenemos que seguir investigando sobre aplicaciones distribuidas e inteligencia artificial. Nuestro sistema nervioso funciona con varios nodos, surgiendo una aplicación distribuidas en neuronas (¿recuerdan a Alan Kay hablando de células con mensajes?)

Otro de los temas que me interesan es tener lenguajes dinámicos que puedan distribuirse fácilmente entre nodos distribuidos (de hecho, me gustó JavaScript con Node.js, porque me libera de implementar eso). Ver:

Lenguajes de Programación, Computación Distribuida, Inteligencia Artificial
Experimentos Distribuidos

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Published Sat, May 4 2013 20:36 by lopez

Comments

# Fabuloso!@ Monday, May 06, 2013 11:37 AM

Angel,

El artículo es genial! Describes el futuro de la web. Está en la línea de la computación con memoria ilimitada que ya habías mencionado.

Muchas gracias

Gabriel Osorio

# re: Aplicaciones Distribuidas y Node.js@ Tuesday, June 18, 2013 1:18 AM

Que bien me vino tu explicación de GA para entender esto

github.com/.../Geo-Projections

Juan Pablo Kutianski

# Cuatro Reuniones en Buenos Aires: Java, Salesforce, Scala, MongoDb@ Friday, August 09, 2013 12:56 PM

El de hoy podría ser un post largo, pero voy a tratar de hacerlo breve. Hay mucho para comentar sobre

Angel "Java" Lopez

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above: