Paradigmas avanzados de computación distribuida En los capítulos anteriores se han explorado un diverso número de paradigmas inicialmente presentados en el Capítulo 3, donde se mostraba una jerarquía de paradigmas repetida aquí en la Figura 12.1. FIGURA 12.1: Los paradigmas de computación distribuida y su nivel de abstracción. Este capítulo presenta alguno de los paradigmas más avanzados. Algunos de ellos están siendo aún investigados, mientras que otros son usados ampliamente en la práctica. Especialmente, este capítulo dará una visión general de los siguientes paradigmas de computación distribuida: sistemas de colas de mensajes, agentes móviles, servicios de red, espacios de objetos y computación colaborativa. Paradigma de sistemas de colas de mensajes El paradigma de sistemas de colas de mensajes, también denominado middleware orientado a mensajes (MOM, message-oriented middleware), es una elaboración del paradigma de paso de mensajes. En este paradigma, un sistema de mensajes actúa como intermediario entre procesos separados e independientes. FIGURA 12.2: Paradigma de sistemas de colas de mensajes. La Figura 12.2 ilustra el paradigma. Los mensajes se envían al sistema de mensajes, que actúa de conmutador de los mismos, encaminándolos a los receptores apropiados. A través de un sistema de mensajes, los procesos intercambian mensajes de forma asíncrona, de una manera desacoplada. Un emisor deposita un mensaje en el sistema, el cual lo reenvía a una cola de mensajes asociada con cada receptor. Una vez que el mensaje se ha enviado, el emisor se libera para poder realizar otras tareas. El mensaje se reenvía por parte del sistema de mensajes hasta los clientes. Cada cliente puede extraer de su cola bajo demanda. Los modelos de sistemas de colas de mensajes se pueden clasificar en dos subtipos, punto-a-punto y publicación/suscripción, que se describen a continuación: Modelo de mensajes punto-a-punto El modelo mostrado en la Figura 12.2 se corresponde con el modelo de mensajes punto-a-punto. En este modelo, un sistema de mensajes redirige un mensaje desde el emisor hasta la cola de mensajes del receptor. A diferencia del modelo básico de paso de mensajes, el modelo de mensaje punto-a-punto proporciona un depósito de los mensajes que permite que el envío y la recepción estén desacoplados. Comparado con el modelo básico de paso de mensajes, este paradigma proporciona una abstracción adicional para operaciones asíncronas: No existe un bloqueo entre emisor y receptor. Modelo de mensajes publicación/suscripción En este modelo, mostrado en la Figura 12.3, cada mensaje se asocia con un determinado tema o evento. Las aplicaciones interesadas en el suceso de un evento específico se pueden suscribir a los mensajes de dicho evento. Cuando ocurre el evento que se aguarda, el proceso publica un mensaje anunciando el evento o asunto. El sistema de cola de mensajes distribuye el mensaje a todos los suscriptores. FIGURA 12.3: El modelo de sistema de mensajes publicación/suscripción. El modelo de mensajes publicación/suscripción ofrece una potente abstracción para multidifusión o comunicación en grupo. La operación publicar permite al proceso difundir a un grupo de procesos, y la operación suscribir permite a un proceso escuchar dicha difusión de mensajes. El paradigma de sistema de mensajes se emplea comercialmentre de forma muy amplia en aplicaciones distribuidas. Existe un gran número de herramientas que dan soporte a este paradigma. Algunas de ellas son: MQ*Series de IBM (rebautizado como WebSphere MQ) [4.ibm.com, 3], Microsoft’s Message Queue (MSMQ) [microsoft.com, 2], Java Message Service (JMS) [java.sun.com, 1] disponible con el J2EE SDK [java.sun.com, 4] versiones 1.3 o superiores. Para aquellos estudiantes interesados en una API que dé soporte a un modelo de sistema de paso de mensajes, JMS es un buen punto de arranque. La Figura 12.4 y la Figura 12.5 muestran un ejemplo de programación con JMS. Un EmisorMensaje envía un mensaje a una cola JMS, mientras que ReceptorMensaje lo recibe de la cola. Para poder apreciar el asíncronismo que el sistema de cola de mensajes proporciona, deberá experimentar arrancando una o varias copias de ReceptorMensaje, y posteriormente el EmisorMensaje. Cada uno de los clientes debe recibir un mensaje (“¡Hola Mundo!”) una vez que se ha enviado. (Nota: Los programas de ejemplo sólo compilarán y ejecutarán en un sistema que tenga instalado JMS; consulte [java.sun.com, 1] para obtener información sobre cómo descargar e instalar JMS.) FIGURA 12.4: Un ejemplo de un emisor de mensajes punto-a-punto. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 /** * La clase EmisorMensaje manda un mensaje "¡Hola Mundo!" a * una cola de mensajes de Java Message System (JMS). Este * programa está preparado para ejecutarse con la clase * ReceptorMensaje, que recibe el mensaje vía el servicio * JMS. * Para ejecutar el programa: El proveedor JMS debe estar * arrancado y se debe haber creado una cola. El nombre de * la cola se debe especificar como una rgumento en la línea * de mandatos al arrancar el programa. * M. Liu, basado en los ejemplos del * tutorial http://java.sun.com/products/jms/tutorial/ */ import javax.jms.*; // para las clases JMS import javax.naming.*; // para las clases JNDI public class EmisorMensaje { public static void main(String[] args) { 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 String nombreCola = null; Context contextoJNDI = null; QueueConnectionFactory fabricaConexiones = null; QueueConnection conexionCola = null; QueueSession sesionCola = null; Queue cola = null; QueueSender emisorCola = null; TextMessage mensaje = null; final int NUM_MSGS; if ( (args.length != 1) ) { System.out.println("Uso: java " + "EmisorMensaje <nombre-cola>"); System.exit(1); } nombreCola = new String(args[0]); System.out.println("Nombre de la cola es " + nombreCola); /* Crea un objeto InitialContext de JNDI si no existe ningunao. */ try { contextoJNDI = new InitialContext(); } catch (NamingException e) { System.out.println("No se ha podido crear el contexto " + "JNDI: " + e.toString()); System.exit(1); } /* Búsqueda de la fábrica de conexiones y la cola. Si alguna de las dos no existe. */ try { fabricaConexiones = (QueueConnectionFactory) contextoJNDI.lookup("QueueConnectionFactory") cola = (Queue) contextoJNDI.lookup(nombreCola); } catch (NamingException e) { System.out.println("Búsqueda JNDI fallida: " + e.toString()); System.exit(1); } try { /* crear conexión */ conexionCola = fabricaConexiones.createQueueConnection(); /* crear sesión */ sesionCola = conexionCola.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); /* crear emisor y objeto mensaje. */ emisorCola = sesionCola.createSender(cola); mensaje = sesionCola.createTextMessage(); /* definir mensaje */ mensaje.setText("¡Hola Mundo!"); System.out.println("Enviando mensaje: " + mensaje.getText()); /* enviar a JMS */ emisorCola.send(mensaje); } catch (JMSException e) { System.out.println("Ha ocurrido una excepción: " + 88 e.toString()); 89 } finally { 90 /* cerrar la cola de conexión */ 91 if (conexionCola != null) { 92 try { 93 conexionCola.close(); 94 } catch (JMSException e) {} 95 } 96 } //fin finally 97 } //fin main 98 } //fin class FIGURA 12.5: Un ejemplo de un receptor de mensajes punto-a-punto. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 /** * La clase ReceptorMensaje se debe usar junto con * la clase EmisorMensaje, la cual manda un mensaje * via the JMS. * Se debe dar el mismo nombre de cola que en EmisorMensaje * por medio de un argumento en la líne de mandatos * M. Liu, basado en los ejemplos del * Tutorial,http://java.sun.com/products/jms/tutorial/ */ import javax.jms.*; import javax.naming.*; public class ReceptorMensaje { public static void main(String[] args) { String nombreCola = null; Context contextoJNDI = null; QueueConnectionFactory fabricaConexiones = null; QueueConnection conexionCola = null; QueueSession sesionCola = null; Queue cola = null; QueueReceiver receptorCola = null; TextMessage mensaje = null; if (args.length != 1) { System.out.println("Uso: java " + "ReceptoMensaje <nombre-cola>"); System.exit(1); } nombreCola = new String(args[0]); System.out.println("Nombre de la cola es " + nombreCola); /* Crea un objeto InitialContext de JNDI si no existe ningunao. */ try { contextoJNDI = new InitialContext(); } catch (NamingException e) { System.out.println("No se ha podido crear el contexto " + "JNDI: " + e.toString()); System.exit(1); } /* Búsqueda de la fábrica de conexiones y la cola. Si alguna de las dos no existe. */ try { fabricaConexiones = (QueueConnectionFactory) contextoJNDI.lookup("QueueConnectionFactory"); cola = (Queue) contextoJNDI.lookup(nombreCola); } catch (NamingException e) { System.out.println("Búsqueda JNDI fallida: " + 55 e.toString()); 56 System.exit(1); 57 } 58 59 try { 60 /* crear conexión */ 61 conexionCola = 62 fabricaConexiones.createQueueConnection(); 63 /* crear sesión de la conexión */ 64 sesionCola = 65 conexionCola.createQueueSession(false, 66 Session.AUTO_ACKNOWLEDGE); 67 /* crear un receptor */ 68 receptorCola = sesionCola.createReceiver(cola); 69 conexionCola.start(); 70 /* recibe el mensaje */ 71 Message m = receptorCola.receive(1); 72 mensaje = (TextMessage) m; 73 System.out.println("Leyendo mensaje: " + 74 mensaje.getText()); 75 } catch (JMSException e) { 76 System.out.println("Ha ocurrido una excepción: " + 77 e.toString()); 78 } finally { 79 if (conexionCola != null) { 80 try { 81 conexionCola.close(); 82 } catch (JMSException e) {} 83 } 84 }//fin finally 85 }//fin main 86 }//fin class Se recomienda a los lectores interesados en más detalles sobre el API de JMS la referencia [java.sun.com,1] que muestra más ejemplos, incluyendo los relativos al modelo publicación/suscripción. Agentes móviles Un agente móvil es un paradigma de computación distribuida que ha interesado a los investigadores desde los 80. Los agentes móviles se han convertido en tecnológicamente viables gracias a las últimas tecnologías y hoy por hoy tienen el potencial de revolucionar las aplicaciones en red. En el contexto de la informática, un agente es un programa software independiente que se ejecuta en representación del usuario. Un agente móvil es un programa que, una vez lanzado por el usuario, puede viajar de ordenador a ordenador autónomamente y puede continuar con sus función incluso si el usuario se desconecta. La Figura 12.6 ilustra el concepto de un agente móvil. FIGIRA 12.6: Un agente móvil viaja de un computador a otro. Arquitectura básica Un agente móvil es un objeto serializable – un objeto cuyos datos al igual que el estado se pueden empaquetar para ser transmitidos por la red. El lector recordará el término empaquetado de datos (data marshaling), introducido por primera vez en el Capítulo 1, como el aplanamiento y codificación de estructuras de datos con el fin de transmitirlas de un computador a otro. Un objeto se puede serializar y transmitir entre dos ordenadores de la misma forma. Una vez llegado, el objeto se reconstruye y se deserializa, con su estado restaurado como al momento de serializarse, y entonces el objeto puede retomar su ejecución en el sistema donde acaba de llegar. La arquitectura básica para dar soporte a agentes móviles se muestra en la Figura 12.7. Un objeto serializable, representando al agente móvil, se lanza en un computador. El agente contiene los siguientes datos: Información de identidad – información que permite identificar al agente. Itinerario – una lista de direcciones de ordenadores que el agente debe visitar. Datos de la tarea – los datos que el agente requiere para realizar sus tareas, o bien datos recogidos por el agente. FIGURA 12.7: Un agente móvil viaja por varios ordenadores. Un agente también lleva consigo la lógica (código) para realizar las tareas. En cada parada, el agente llega a un servidor. A través del servidor de agentes, el agente móvil usa los recursos locales para realizar sus tareas. Como parte de la arquitectura, se necesita un servicio de directorio que permita al agente buscar el servidor en cada parada. Cuando el agente ha concluido su tarea en un sitio, el objeto agente se serializa, y, con la ayuda de un servidor de agentes, el objeto se transporta al siguiente ordenador del itinerario. Hay varios entornos que se han desarrollado para proporcionar arquitecturas de soporte a agentes móviles, por ejemplo Aglet [Lange and Oshima,5; aglets.sourceforge.net, 6], Concordia [merl.com, 7], y Grasshopper [grasshopper.de, 8]. Por simplicidad, sin embargo, se mostrará el concepto de agente móvil usando objetos serializables y Java RMI. (Nota: La implementación presentada vale para demostrar el concepto de agente móvil. Para verdaderos desarrollos, existen consideraciones –a discutir – que la demostración no trata.) La Figura 12.8 presenta la interfaz Java de un agente; la implementación de la interfaz se ve en la Figura 12.9. FIGURA 12.8: InterfazAgente.java. // Una interfaz para un objeto transportable // que representa un agente móvil // M. Liu import java.io.Serializable; public interface InterfazAgente extends Serializable { void ejecuta(); } FIGURA 12.9: Agente.java. 1 2 3 4 5 6 7 // Una implementación de un agente móvil import import import import import java.io.*; java.util.*; java.rmi.*; java.rmi.registry.Registry; java.rmi.registry.LocateRegistry; 8 9 public class Agente implements InterfazAgente { 10 11 int indiceNodo; // cuál es el siguiente ordenador a visitar 12 String nombre; 13 Vector listaNodos; // el itinerario 14 int puertoRMI = 12345; 15 16 public Agente(String miNombre, Vector laListaComputadoras, 17 int elPuertoRMI ) { 18 nombre = miNombre; 19 listaNodos = laListaNodos; 20 indiceNodo = 0; 21 puertoRMI = elPuertoRMI; 22 } 23 24 // Este método define las tareas que realiza el agente 25 // móvil una vez que llega a un servidor. 26 public void ejecuta() { 27 String actual, siguiente; 28 dormir (2); // pausa para poder visualizarlo 29 System.out.println("¡Aquí el agente 007!"); 30 actual = (String) listaNodos.elementAt(indiceNodo); 31 indiceNodo++; 32 if (indiceNodo < listaNodos.size()) { 33 // si hay más computadoras que visitar 34 siguiente = (String) listaNodos.elementAt(indiceNodo); 35 dormir (5); // pausa para poder visualizarlo 36 try { 37 // Localiza el registro RMI en el siguiente nodo 38 Registry registro = LocateRegistry.getRegistry 39 ("localhost", puertoRMI); 40 ServerInterface h = (ServerInterface) 41 registro.lookup(siguiente); 42 System.out.println("Buscando " + siguiente + 43 " en " + actual + " completado "); 44 dormir (5); // pausa para poder visualizarlo 45 // Pide al servidor del siguiente nodo que reciba a 46 // este agente. 47 h.recibe(this); 48 } // fin try 49 catch (Exception e) { 50 System.out.println 51 ("Excepción en el ejecuta del Agente: " + e); 52 } 53 } // fin if 54 else { //si se han hecho todas las paradas 55 dormir (5); // pausa para poder visualizarlo 56 System.out.println("El Agente 007 ha regresado a casa"); 57 dormir (5); // pausa para poder visualizarlo 58 } 59 } 60 61 // El método dormir suspende la ejecución de este objeto 62 // un número determinado de segundos. 63 static void dormir (double time ){ 64 try { 65 Thread.sleep( (long) (time * 1000.0)); 66 } 67 catch (InterruptedException e) { 68 System.out.println ("excepción en dormir); 69 } 70 } // fin dormir 71 72 } // fin class Agente El núcleo de la implementación del agente (Figura 12.9) está en su constructor (líneas 16–22) y en su método ejecuta (líneas 26–59). El constructor inicializa los datos de estado del agente, incluyendo el itinerario y el índice al siguiente computador a visitar en dicho itinerario. El método ejecuta contiene la lógica de las tareas que se espera que el agente realice en cada ordenador. En esta demo, las tareas se resumen en la salida de un simple mensaje (línea 56) y en la búsqueda del servidor de agentes de la siguiente parada (líneas 32–53). La implementación de servidor de agentes se muestra en las Figuras 12.10 y 12.11. FIGURA 12.10: InterfazServidor.java. // Fichero de interfaz del servidor de agentes - M. Liu import java.rmi.*; public interface InterazServidor extends Remote { public void recibe(Agente h) throws java.rmi.RemoteException; } El servidor, por su parte, se registra en el registro RMI y proporciona un método, recibe, que el agente móvil invoca cuando está preparado para transportarse al servidor. El método imprime un mensaje que anuncia la llegada del agente, y posteriormente llama al método ejecuta del agente. Nótese que el método recibe acepta como argumento el objeto agente serializable. FIGURA 12.11: Servidor.java. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 // Una import import import import import import implemnetación de un servidor agentes java.rmi.*; java.rmi.server.*; java.rmi.registry.Registry; java.rmi.registry.LocateRegistry; java.net.*; java.io.*; public class Servidor extends UnicastRemoteObject implements InterfazServidor{ static int puertoRMI = 12345; public Servidor() throws RemoteException { super(); } public void recibe(Agent h) throws RemoteException { dormir (3); // pausa para poder visualizarlo System.out.println ("*****El Agente " + h.name + " ha llegado." ); h.ejecuta(); } public static void main(String args[]) { InputStreamReader is = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(is); String s; String miNombre = "servidor" + args[0]; //Nota: Los servidores se espera que se arranquen con los //argumentos 1, 2 y 3 de la línea de mandatos respectivamente, try{ System.setSecurityManager(new RMISecurityManager()); Servidor h = new Servidor(); Registry registro = LocateRegistry.getRegistry(puertoRMI); registro.rebind( miNombre, h); 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 System.out.println("*******************************"); System.out.println(" Agente " + miNombre + " listo."); System.out.println("*******************************"); }// fin try catch (RemoteException re) { System.out.println("Excepción en el main del Servidor: " + re); } // fin catch } // fin main // El método dormir suspende la ejecución de este objeto // un número determinado de segundos. static void dormir (double time ){ try { Thread.sleep( (long) (time * 1000.0)); } catch (InterruptedException e){ System.out.println ("excepción en dormir"); } } // fin dormir } // fin class Para finalizar, se necesita un programa cliente que cree una instancia y lance al agente hacia la primera parada del itinerario. La implementación se muestra en la Figura 12.12. FIGURA 12.12: Cliente.java. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 //Cliente.java - programa cliente que lanza al agente móvil // M. Liu import import import import import java.io.*; java.util.*; java.rmi.*; java.rmi.registry.Registry; java.rmi.registry.LocateRegistry; public class Cliente { static int puertoRMI = 12345; public static void main(String args[]) { System.setSecurityManager(new RMISecurityManager()); try { Registry registro = LocateRegistry.getRegistry ("localhost", puertoRMI); ServerInterface h = (ServerInterface) registro.lookup("servidor1"); System.out.println ("Lookup for servidor1 completed " ); System.out.println("***Buen viaje, " + " agent 007."); Vector listaNodos = new Vector(); listaNodos.addElement("servidor1"); listaNodos.addElement("servidor2"); listaNodos.addElement("servidor3"); Agent a = new Agent("007", listaNodos, puertoRMI); h.receive(a); System.out.println("***Buen trabajo, 007"); } catch (Exception e) { System.out.println("Excepción en main: " + e); } } //fin main } //fin class Como paradigma de computación distribuida, los agentes móviles son radicalmente diferentes al resto de paradigmas que se han visto. En otros paradigmas, procesos independientes colaboran intercambiando datos sobre sus enlaces de red. Con los agentes móviles, un proceso se transmite, llevando consigo los datos a compartir y visitando la lista de procesos de su itinerario. La Figura 12.13 muestra esta característica discriminatoria. FIGURA 12.13: Contraste entre el paradigma de agentes móviles y los paradigmas convencionales. Ventajas de los agentes móviles El paradigma de agentes móviles proporciona las siguientes ventajas: Los agentes móviles permiten un uso eficiente y económico de los canales de comunicación, que pueden ser de ancho de banda reducido, latencia elevada, y pudiendo ser proclives a errores. En contra, los paradigmas convencionales requieren el intercambio repetido de paquetes de datos sobre la red. Si esto afecta a grandes volúmenes de datos, el consumo de ancho de banda puede ser considerable, con el consecuente retardo en el tiempo de respuesta o la latencia. Por medio de agentes móviles, un único objeto se serializa y se transmite por la red, potencialmente reduciendo el consumo de ancho de banda. Además, ya que un agente móvil sólo se necesita transportar entre dos nodos una vez, la probabilidad de fallo debido a un error de comunicación se reduce. Por estas dos razones, los agentes móviles son especialmente interesantes para enlaces de red inalámbricos. Los agentes móviles permiten el uso de dispositivos de comunicación portátiles y de bajo coste para realizar tareas complejas incluso cuando el dispositivo está desconectado de la red. Un agente se puede lanzar desde cualquier dispositivo que soporte la arquitectura necesaria. Una vez lanzado, el agente se desacopla de su origen y es capaz de realizar las tareas de forma independiente de su dispositivo inicial. Con una implementación apropiada, un agente móvil puede tener la inteligencia de saltarse nodos que fallen o de buscar refugio momentáneo en nodos fiables. Los agentes móviles permiten operaciones asíncronas y una verdadera descentralización. La ejecución de un agente móvil está desacoplada de su servidor original y de los servidores participantes. Las tareas se realizan en cada uno de los nodos individuales de una manera asíncrona, y no resulta necesario que ninguno de los participantes asuma el papel de coordinador. A pesar de las ventajas ofrecidas por los agentes móviles, el paradigma no se ha desarrollado de forma amplia en aplicaciones industriales o comerciales. Una de las principales pegas del paradigma es que puede suponer un riesgo de seguridad para los participantes. Existen consideraciones para ambos, servidores y agentes móviles. Desde el punto de vista de los servidores que reciben un agente, agentes maliciosos o no autorizados pueden hacer un uso fraudulento y destruir recursos locales. Sin restricciones, un agente malicioso puede causar un caos en el servidor, de igual manera que lo hacen los virus. Desde el punto de vista de un agente, servidores maliciosos pueden destruir o alterar los datos del agente o su lógica. Por ejemplo, el itinerario de un agente móvil se podría alterar o destruir por un servidor malicioso, de forma que el agente no pudiese continuar con su viaje. O, los datos almacenados por el agente se podrían alterar de forma que el agente se llevase información errónea. La seguridad en sistemas de agentes es un área de investigación muy activa [mole.informatik.uni-stuttgart.de, 9]. Algunas de las contramedidas que se han propuesto son: Autenticación. Un agente debe autentificarse ante el servidor, y un servidor de agentes debe autentificarse ante el agente. Cifrado. Un agente debe cifrar sus datos sensibles. Acceso a recursos. Un servidor forzará un estricto control de acceso a sus recursos. Sistemas basados en entornos para agentes móviles Hay disponible un gran número de sistemas basados en entornos de desarrollo para agentes móviles. Además de proporcionar la arquitectura para transportar agentes móviles, estos sistemas también tratan cuestiones relativas a la seguridad, el soporte transaccional, y la gestión de agentes. Los lectores que estén interesados en este tema pueden buscar en referencias como [trl.ibm.com, 6], [concordiaagents.com, 7], y [grasshopper.de, 8]. Servicios de red En el paradigma de servicios de red [Edwards, 11], la red se ve como una infraestructura para servicios, que “puede incluir aplicaciones, ficheros, bases de datos, servidores, sistemas de información, y dispositivos, tales como electrodomésticos móviles, almacenamiento o impresoras.” La Figura 12.14 ilustra este paradigma. Una aplicación cliente puede utilizar uno o varios de estos servicios. Los servicios se pueden añadir y eliminar de la red de forma autónoma, y los clientes pueden localizar los servicios disponibles, a través de un servicio de directorio. El protocolo SOAP (Simple Object Access Protocol), que se vio en el Capítulo 11, se basa en este paradigma. Jini, que es anterior a SOAP, es un conjunto de herramientas basado en este paradigma. FIGURA 12.14: El paradigma de servicios de red. Las Figuras 12.15 y 12.16 muestran la sintaxis de la implementación de un proveedor de servicios en Jini, que proporciona el método decirHola(). La Figura 12.17 muestra la implementación de un cliente de dicho servicio. Nótese que Jini hace uso de RMI, que ya se vio en los Capítulos 7 y 8. FIGURA 12.15: InterfazServidorHola.java 1 // Interfaz para el ServidorHola 2 3 import java.rmi.*; 4 5 public interface InterfazServidorHola extends Remote { 6 public String decirHola() throws RemoteException; 7 } FIGURA 12.16: ServidorHola.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 // Un ejemplo de servidor Jini sencillo // M. Liu, basado en el ejemplo "Noel's Nuggets" [cc.gatech.edu, 14] import net.jini.core.entry.*; import net.jini.core.lookup.*; import net.jini.core.discovery.*; import net.jini.lookup.entry.*; import com.sun.jini.lookup.*; import com.sun.jini.lease.*; import java.io.*; import java.rmi.*; import java.rmi.server.*; public class ServidorHola extends UnicastRemoteObject implements InterfazServidorHola, ServiceIDListener { public ServidorHola () throws RemoteException { super(); } public String decirHola () throws RemoteException { return ("¡Hola Mundo!"); } // Este método está a la escucha de ServiceID de parte del // servicio de directorio, cuando el ID esté disponible. public void serviceIDNotify (ServiceID id) { System.out.println (" ServiceID recibido: " + id); } public static void main (String[] args){ ServidorHola servidor; Entry[] atributos; JoinManager manager; try { System.setSecurityManager (new RMISecurityManager ()); // Crea los atributos como un vector de objetos // Entry que describen este servicio, y // se registra en el servicio de búsqueda vía // el JoinManager. El ServiceID, cuando esté disponible, // le será indicado por medio del listerner del ServiceID. atributos = new Entry[1]; atributos[0] = new Name("ServidorHola"); servidor = new ServidorHola(); manager = new JoinManager ( servidor, atributos, servidor, new LeaseRenewalManager() ); } catch (Exception ex) { ex.printStackTrace( ); } } //fin main } //fin class FIGURA 12.17: Cliente Jini 1 2 3 4 5 // Un ejemplo de un cliente Jini // M. Liu, basado en el ejemplo "Noel's Nuggets" [cc.gatech.edu, 14] import net.jini.core.entry.*; import net.jini.core.lookup.*; 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import import import import net.jini.core.discovery.*; net.jini.lookup.entry.*; com.sun.jini.lookup.*; java.rmi.*; class ClienteHola{ public static void main (String[] args){ Entry[] atributos; LookupLocator buscador; ServiceID id; ServiceRegistrar registrar; ServiceTemplate plantilla; InterfazServidorHola servidorHola; try { System.setSecurityManager (new RMISecurityManager ()); // Localizar el servcio de búsqueda de Jini buscador = new LookupLocator("jini://localhost"); // Buscar el ServiceRegistrar registrar = buscador.getRegistrar(); // Búsqueda del servicio atributos = new Entry[1]; atributos[0] = new Name ("ServidorHola"); plantilla = new ServiceTemplate (null, null, atributos); servidorHola = (InterfazServidorHola) registrar.buscador(plantilla); // Invocar el método del servicio System.out.println(servidorHola.decirHola()); } catch (Exception ex) { ex.printStaceTrace( ); } } } La introducción que se ha presentado apenas toca los aspectos fundamentales de Jini y no hace justicia a las sofisticadas capacidades proporcionadas por este conjunto de herramientas. Un concepto interesante en Jini es el uso de “alquileres” (“leases”) (línea 49 en la Figura 12.16). En un sistema Jini, cada servicio se asocia a un gestor de renovación de alquileres. Cuando un cliente localiza un servicio Jini a través de un servicio de búsqueda, el propio servicio de búsqueda actúa como elemento que concede periodos de alquiler para el servicio. El cliente puede realizar peticiones mientras que su alquiler no haya expirado. El uso de alquileres permite al sistema proporcionar tolerancia a fallos de una forma más eficiente. (Los fallos en sistemas distribuidos y la tolerancia a fallos se presentaron en el Capítulo 1.) Espacios de objetos Esta sección muestra el paradigma que proporciona el mayor nivel de abstracción: los espacios de objetos. El paradigma de espacios de objetos tiene su origen en el Espacio de Tuplas de Linda (Linda Tuplespace) desarrollado por David Gelernter [Ahuja, Carrier, y Gelernter, 17] y es la base de los sistemas de Pizarras en el campo de la Inteligencia Artificial [Corkill, 15]. En el paradigma de espacios de objetos, un espacio es un repositorio para objetos, compartido y accesible por red. En lugar de comunicarse entre sí, los procesos se coordinan intercambiando objetos a través de uno o varios espacios. La Figura 12.18 ilustra este concepto. Los procesos interactúan compartiendo los datos de estado en uno o más objetos o actualizando el estado de los objetos según sus necesidades. FIGURE 12.18: El paradigma de espacio de objetos. Un proceso puede depositar un objeto en un espacio. A diferencia de los modelos de mensajes, los procesos no modifican los objetos en el espacio ni invocan los métodos de los objetos directamente. Un proceso que desea acceder a un objeto utilizará un servicio de directorio para localizar el objeto en el espacio. Para modificar el objeto, un proceso debe retirarlo explícitamente, actualizarlo y reinsertarlo en el espacio. El paradigma de espacios de objetos es una área activa de investigación. Para poder experimentar con este paradigma existe un conjunto de herramientas basado en Java, JavaSpaces, que proporciona un entorno de trabajo para espacios de objetos. En el API de JavaSpaces, se proporciona una interfaz llamada Entry. Un objeto Entry puede escribirse o leerse del espacio. De la Figura 12.19 a la 12.21 se muestra un programa de ejemplo sencillo que utiliza un objeto Entry que contiene un mensaje “¡Hola Mundo!” y un contador. El servidor crea el objeto Entry y lo escribe en el espacio de objetos por defecto. El cliente lee (toma) el objeto del espacio e incrementa el contador, antes de escribirlo de nuevo en el espacio. Nótese que JavaSpaces utiliza el API de Jini presentada en la sección 12.3. FIGURA 12.19: Un ejemplo de una clase Entry de JavaSpaces. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 // Una clase Entry de ejemplo para un objeto de espacio JavaSpaces // M. Liu, basado en el ejemplo proporcionado en: // JAVASPACES PRINCIPLES, PATTERNS, AND PRACTICE [18] import net.jini.core.entry.Entry; public class ObjetoDeEspacio implements Entry { public String mensaje; public Integer contador; //Es necesario que sea un objeto public ObjetoDeEspacio( ) { } public ObjetoDeEspacio(String mensaje) { this.mensaje = mensaje; contador = 0; } public String toString( ) { return mensaje + " leido " + contador + " veces."; } public void incremento( ) { contador = new Integer(contador.intValue() + 1); } } //fin class FIGURA 12.20: Un programa de ejemplo que inicializa un objeto JavaSpaces. 1 // Un programa que inicializa un Entry de JavaSpaces. 2 // M. Liu, basado en el ejemplo proporcionado en: 3 // JAVASPACES PRINCIPLES, PATTERNS, AND PRACTICE [18] 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import net.jini.core.lease.Lease; import net.jini.space.JavaSpace; public class HolaMundo { public static void main(String[] args) { try { SpaceObject mens = new SpaceObject("¡Hola Mundo!"); JavaSpace espacio; espacio = (JavaSpace)space(); espacio.write(mens, null, Lease.FOREVER); SpaceObject plantilla = new SpaceObject(); while (true) { SpaceObject resultado = (SpaceObject) espacio.read(plantilla, null, Long.MAX_VALUE); System.out.println(resultado); Thread.sleep(1000); } //fin while } catch (Exception ex) { ex.printStackTrace(); } } } //fin class FIGURA 12.21: Un ejemplo de un cliente JavaSpaces. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // Un programa cliente que accede a un Entry de JavaSpaces // M. Liu, basado en el ejemplo proporcionado en: // JAVASPACES PRINCIPLES, PATTERNS, AND PRACTICE [18] import net.jini.core.lease.Lease; import net.jini.space.JavaSpace; public class ClienteHolaMundo { public static void main(String[] args) { try { JavaSpace espacio = (JavaSpace)space(); SpaceObject plantilla = new SpaceObject(); // Retira el objeto del espacio repetidas veces, // lo modifica, y lo deposita de nuevo en el espacio while (true) { // Lee el objeto del espacio SpaceObject result = (SpaceObject) espacio.take(plantilla, null, Long.MAX_VALUE); result.incremento(); // Escribe el objeto en el espacio espacio.write(result, null, Lease.FOREVER); Thread.sleep(1000); }//fin while } catch (Exception ex) { ex.printStackTrace(); } } } //fin class Es especialmente interesante que la exclusión mutua es una característica interna de diseño del paradigma de espacios de objetos, ya que un objeto compartido sólo puede ser accedido por un participante a la vez. La referencia [Ahuja, Carrier, y Gelernter, 17] proporciona excelentes ejemplos que ilustran las aplicaciones que se pueden construir usando este paradigma. A los lectores interesados en JavaSpaces se les recomienda consultar las referencias [java.sun.com, 18], [jiniworld.net, 19], [onjava.com, 20], y [Alter, 23]. Tendencias futuras Este capítulo ha presentado un gran número de paradigmas avanzados de computación distribuida, muchos de los cuales se encuadran dentro del área de investigación. Existen verdaderamente más paradigmas y herramientas que irán surgiendo. Por ejemplo, peer-to-peer es un paradigma que recientemente ha recibido mucha atención; se han desarrollado varios sistemas y API para dar soporte a este paradigma, incluyendo el proyecto JXTA [jxta.org, 16]. Otro paradigma, computación colaborativa, usa el concepto de canales para permitir que procesos independientes colaboren en tiempo real; el entorno JSDT (Java Shared Data Toolkit) [java.sun.com, 21] se puede utilizar “para crear aplicaciones de red, como pizarras compartidas o entornos de chat. También se puede utilizar para hacer presentaciones remotas, simulaciones compartidas, y distribuir fácilmente datos para trabajos en grupo.” Se espera que usted, como lector del libro, haya adquirido los conceptos básicos y comprensión que le permitirá explorar paradigmas nuevos con los que no esté familiarizado así como nuevas API de computación distribuida. Resumen Este capítulo ha proporcionado una visión general de los siguientes paradigmas avanzados de computación distribuida: El paradigma de Sistemas de Colas de Mensajes, que proporciona emisión y recepción de mensajes de forma asíncrona a través del uso de colas de mensajes. Un entorno que da soporte a este paradigma es Java Message System (JMS). El paradigma de agentes móviles, que da soporte al uso de programas transportables. A pesar de que los agentes móviles se pueden implementar directamente usando RMI, se recomienda el uso de entornos como Aglet, Concordia, y Grasshopper. El paradigma de servicios de red, el cual muestra la red como una federación de proveedores de servicios y de consumidores de los mismos. Una aplicación puede consumir uno o más recursos, según lo requiera. El protocolo SOAP (Simple Object Access Protocol) se utiliza para este paradigma. Jini de Java es un conjunto de herramientas muy sofisticado que hace uso de un concepto interesante que son los alquileres de servicios. El paradigma de espacios de objetos, el cual proporciona espacios lógicos donde los objetos pueden depositarse y retirarse por elementos colaboradores. JavaSpaces es un conjunto de herramientas que soportan este paradigma. Hay otros paradigmas y otras tecnologías que no se han cubierto, incluyendo peer-topeer y computación colaborativa. Ejercicios 1. Siga las instrucciones de [java.sun.com, 1] para instalar JMS (Java Message System) en su ordenador. a. Experimente con el ejemplo HolaMundo mostrado en las Figuras 12.4 y 12.5. Compile los ficheros fuente Arranque un receptor, luego un emisor. Describa y explique el comportamiento. Arranque dos receptores, luego un emisor. Describa y explique el comportamiento. b. Repita parte del experimento con el ejemplo punto-a-punto proporcionado en [java.sun.com, 1]. c. Repita parte del experimento con el ejemplo publicación/suscripción proporcionado en [java.sun.com, 1]. 2. Describa una aplicación que use una o más colas de mensajes punto-a-punto. Explique cómo la aplicación saca partido del uso de cola(s) de mensajes en términos de facilidad de implementación. 3. Describa una aplicación que use una o más colas de mensajes publicación/suscripción. Explique cómo la aplicación saca partido del uso de cola(s) de mensajes en términos de facilidad de implementación. 4. Siga las instrucciones del fichero README de la carpeta de ejemplos de agentes móviles para ejecutar la demo de agentes móviles mostrada en las Figuras de la 12.8 a las 12.12. Describa y explique el comportamiento. 5. Investigue el entorno de trabajo Aglets [trl.ibm.com, 6], un entorno para agentes móviles. a. ¿Cómo se proporciona transporte de código dentro del entorno Aglet? b. ¿Qué otros servicios proporciona el entorno Aglet además del de transporte de código? c. Descargue e instale el paquete, experimente con los ejemplos proporcionados. 6. Considere las siguientes aplicaciones listadas a continuación. Describa cómo cada uno de estos paradigmas (sistemas de colas de mensajes, agentes móviles y espacios de objetos) pueden aplicarse para desarrollar cada aplicación. Nótese que no se le pide que realice la implementación. Su descripción debe enmarcarse en términos del paradigma genérico, no de una API específica: Las aplicaciones a considerar son: a. Monitorización de la carga de mercancías a transportar por un vehículo en camino entre almacenes y tiendas. b. Una sala de chat. c. Una subasta on-line. d. Un servicio que permita a su universidad proporcionar información de los cursos a los estudiantes. La información se actualiza frecuentemente. 7. Investigue sobre JXTA [jxta.org, 16] y escriba un informe del soporte para el paradigma peer-to-peer. Incluya en su informe una descripción de la arquitectura, del API, y programas de ejemplo. ¿Dónde pondría este paradigma dentro de la jerarquía de paradigmas distribuidos? 8. Investigue sobre JSDT (Java Shared Data Toolkit) [pandonia.canberra.edu.au, 22] y escriba un informe sobre el paradigma al que da soporte. Incluya en su informe una descripción de la arquitectura, del API, y programas de ejemplo. ¿Dónde pondría este paradigma dentro de la jerarquía de paradigmas distribuidos? Referencias 1. Java Message Service Tutorial, http://java.sun.com/products/jms/tutorial/ 2. Microsoft MSMQ Home Page, http://www.microsoft.com/msmq/default.htm 3. IBM’s MQ-Series, http://www-4.ibm.com/software/ts/mqseries/ 4. Java™ 2 Platform, Enterprise Edition, http://java.sun.com/j2ee/ 5. D. Lange y M. Oshima. “Seven Good Reasons for Mobile Agents.” Communications of the ACM, March 1999. 6. IBM Aglet. http://www.trl.ibm.com/aglets/ 7. Concordia. http://www.concordiaagents.com/ 8. Grasshopper 2, http://www.grasshopper.de/index.html 9. Security in mobile agent systems, http://mole.informatik.uni-stuttgart.de/security.html 10. Jini™ Network Technology, http://www.sun.com/jini/ 11. Keith Edwards. Core Jini. Upper Saddle River, NJ: Prentice Hall PTR, 2000. 12. Jan Newmarch’s Guide to JINI Technologies, http://jan.netcomp.monash.edu.au/java/jini/tutorial/Jini.xml 13. Jini Planet, http://www.kedwards.com/jini/ 14. Noel’s Nuggets, http://www.cc.gatech.edu/~kris/cs7470/nuggets/: Jini tutorial, with examples and instructions. 15. Daniel D. Corkill. “Blackboard Systems.” AI Expert 6, no. 9 (September 1991): 40–47. 16. Project JXTA, http://www.jxta.org/ 17. Sudhir Ahuja, Nicholas Carriero, and David Gelernter. “Linda and Friends.” Computer, August 1986: 26–34. 18. Eric Freeman, Susanne Jupfer, y Ken Arnold. JavaSpaces: Principles, Patterns, and Practice. Reading, MA: Addison-Wesley, 1999. http://java.sun.com/docs/books/jini/javaspaces/ 19. The Nuts and Bolts of Compiling and Running JavaSpaces Programs, http://www.jiniworld.net/document/javaspace/The Nuts and Bolts of Compiling and Running JavaSpaces(TM).htm