Paso de mensajes con RMI

Programación distribuida

No existe memoria común, hay comunicaciones, no existe un estado global del sistema, grado de transparencia, escalablas y reconfigurables.

 Modelos de programación distribuida

  • Paso de mensajes (send y receive)
  • RPC
    • Stubs cliente y servidor
    • Visión en java es RMI
  • Objetos distribuidos
    • DCOM de Microsoft
    • Jini de SUN
    • CORBA de OMG

Modelos de paso de mensajes

Mecanismo para comunicar y sincronizar entidades concurrentes que no pueden o no quieren compartir memoria. Se hace uso de llamadas send y receive.

Tipología de llamadas:

  • BLoqueadas - no bloqueadas
  • Almacenadas - no almacenadas
  • Fiables - no fiables

En UNIX, pipes con y sin nombres. En Java:

  • Sockets, clases Socket y ServerSocket
  • Canales, CTJ (communicating Threads for Java)

Modelo Romote Procedure Call (RPC)

Mayor nivel de abstracción, llamadas a procedimientos local o remoto indistintamente.

Necesita de stubs/skeletons (ocultamiento), y registro del serviio (servidor de nombres).

En UNIX:

  • Biblioteca rpc.h
  • Representación estándar XDR
  • Automatización parcial, especificación-compilador rpcgen

En java:

  • Objetos remotos, serialización
  • Paquete java.rmi
  • Automatización parcial, interfaz-compilador rmic

Nuestro objetivo será efectuar llamadas como si tuvieran carácter local a un método remoto, enmascarando los aspectos relativos a comunicaciones.

Stubs

Efectúan el marshalling/unmarshalling de parámetros, bloquean al cliente a la espera del resultado. Transparecia de ejecución remota. Interface con el nivel de red (TCP, UDP).

Generación

De forma manual o de forma automática.

Especificación formal de interfaz, software específico, rpcgen (C-UNIX) / rmic (Java).

Nota sobre RPC en C

Especificación de la interfaz del servidor remoto en un fichero de especificación .x

/* rand.x */
program RAND_PROG{
    version RAND_VER{
        void inicia_aleatorio(long)=1;
        double obtener_aleat(void)=2;
    }=1;
    }=0x3111111;

Generación automática de resguardos, rpcgen rand.x

Distribuir y compilar ficheros entre las máquinas implicadas.

Necesario utilizar representación XDR. Biblioteca xdr.h.

RMI en Java

Permite disponer de objetos distribuidos utilizando Java. Un objeto distribuido se ejecuta en una JVM diferente o remota.

Objetivo, lograr una referencia al objeto remoto que permita utilizarlo como si el objeto se estuviera ejecutando sobre la JVM local.

Similar a RPC, si bien el nivel de abstracción es más alto, se puede generalizar a otros lenguajes utilizando JNI. RMI pasa los parámetros y valores de retorno utilizando serialización de objetos.

RPCRMI
Carácter y estructura de diseño procideimentalCarácter y estructura de diseño orientada a objetos
Es dependiente del lenguajeEs dependiente del lenguaje
Utiliza la representación externa de datos XDRUtiliza la serialización de objetos en Java
El uso de punteros requiere el manejo explícito de los mismosEl uso de referencias a objetos locales y remotos es automático
NO hay movilidad de códigoEl código es móvil, mediante el uso de bytecodes

Llamadas locales vs. llamadas remotas

Un objeto remoto es aquél que se ejecuta en una JVM diferente situada potencialente en un host distinto.

RMI es la acción de invocar a un método de la interfaz de un objeto remoto.

// ejemplo de llamada a metodo local
int dato;
dato = Suma (x,y);

//ejemplo de llamada a metodo remoto (no completo)
IEjemploRMI1 ORemoto =
    (IEjemploRMI1)Naming.lookup("//sargo:2005/EjemploRMI1";

ORemoto.Suma(x,y);

Arquitectura RMI

  • Servidor:

    • Debe extender RemoteObject
    • Debe implementar una interfaz diseñada previamente
    • Debe tener como mínimo un constructor (nulo) que lance una excepción RemoteException
    • El método main debe lanzar un gestor de seguridad, así mismo éste crea los objetos remotos
  • Cliente:

    • De objetos remotos se comunican con interfaces remotas (diseñadas antes de), estos son pasados por referencia
    • Los que llamen a métodos remotos deben manejar excepciones

El compilador de RMI (rmic) genera el stub y el skeleton.

Fases de diseño de RMI

  • Escribir el fichero de la interfaz remota (public y extender de Remote)
  • Declarar todos los métodos que el servidor remoto ofrece, pero NO los implementa. Se indica nombre, parámentros y topo de retorno.
  • Todos los métodos de la interfaz remota lanzan obligatoriamente la excepción RemoteException
  • El propósito de la interface es ocultar la implementación de los aspectos relativos a los métodos remotos, de esta forma, cuando el cliente logra una referencia a un objeto remoto, en realidad obtiene una referencia a una interfaz
  • Los clientes envían sus mensajes a los métodos de la interfaz
/**
* Ejemplo de implementacion del interfaz remoto para un RMI
*/
import java.rmi.*;
import java.rmi.server.*;
import java.rmi.registry.*;
import java.net.*;
// el servidor debe siempre extender esta clase
// el servidor debe simpre implementar la interfaz
// remota definida con caracter previo
public class EjemploRMI1 extends UnicastRemoteObject implements IEjemploRMI1 {
    public int Suma(int x, int y) throws RemoteException{
        return x + y;
    }

    public int Resta(int x, int y) throws RemoteException {
        return x - y;
    }

    public int Producto(int x, int y) throws RemoteException {
        return x * y;
    }

    public float Cociente(int x, int y) throws RemoteException {
        if (y == 0) return 1 f;
        else return x / y;
    }
    //es necesario que haya un constructor (nulo) como minimo, ya que debe lanzar RemoteException
    public EjemploRMI1() throws RemoteException {
        //super(); invoca automaticamente al constructor de la superclase
    }

    //el metodo main siguiente realiza el registro del servicio
    public static void main(String[] args) throws RemoteException {
        // crea e instala un gestor de seguridad que soporte RMI.
        // el usado es distribuido con JDK. Otros son posibles.
        // En esta ocasion trabajamos sin el.
        // System.setSecurityManager(
        // new RMISecurityManager());
        //Se crea el objeto remoto. Podriamos crear mas si interesa.
        IEjemploRMI1 ORemoto = new EjemploRMI1();

        //Se registra el servicio
        Naming.bind("Servidor", ORemoto);
        System.out.println("Servidor Remoto Preparado");
    }
}

Stub y Skeleton

Es necesario que en la máquina remota donde se aloje el servidor se sitúen también los ficheros stub y skeleton. Con todo ello disponible, se lanza el servidor llamando a JVM del host remoto, previo registro en un DNS.

Registro

Método Naming.bind("Servidor", ORemoto); para registrar el objeto remoto creado requiere que el servidor de nombres esté activo. Dicho servidor se activa con start rmiregistry en Win32, o rmiregistry & en Unix.

Se puede indicar como parámetro el puerto que escucha, si no se indica, por defecto es el puerto 1099. El parámetro puede ser el nombre de un host como por ejemplo:

Naming.bind("//sargo.uca.es:2005/Servidor", ORemoto);

Cliente

El objeto cliente procede siempre creando un objeto de interfaz remota.

Posteriormente efectúa una llamada al método Naming.lookup cuyo parámetro es el nombre y puerto del host remoto junto con el nombre del servidor.

Naming.lookup devuelve una referencia que se convierte a una referencia a la interfaz remota.

A partir de aquí, a través de esa referencia el programador puede invocar todos los métodos de esa interfaz remota como si fueran referencias a objetos en la JVM local.

// Ejemplo de implementacion de un cliente para RMI
import java.rmi.*;
import java.rmi.registry.*;
public class ClienteEjemploRMI1 {
    public static void main(String[] args) throws Exception {
        int a = 10; int b = -10;
        //En esta ocasion trabajamos sin gestor de seguridad //System.setSecurityManager(new RMISecurityManager());
        //Se obtiene una referencia a la interfaz del objeto remoto //SIEMPRE debe convertirse el retorno del metodo Naming.lookup //a un objeto de interfaz remoto
        IEjemploRMI1 RefObRemoto = (IEjemploRMI1) Naming.lookup("//localhost/Servidor");
        System.out.println(RefObRemoto.Suma(a, b));
        System.out.println(RefObRemoto.Resta(a, b));
        System.out.println(RefObRemoto.Producto(a, b));
        System.out.println(RefObRemoto.Cociente(a, b));
    }
}

Evoluciones reseñables

A partir del JDK 1.2 la implementación de RMI no necesita skeletons, el stub estará en ambos lados. A partir del JDK 1.5 se incorpora Generación dinámica de resguardos.

Características avanzadas

Serialización de objetos

Serializar un objeto es convertirlo en una cadena de bits, posteriormente restaurada en el objeto original. Es útil para enviar objetos complejos en RMI.

Los tipos primitivos se serializan automáticamente, las clases contenedoras también. Los objetos complejos deben implementar la interfaz Serializable (es un flag) para poder ser serializados.

Gestión de seguridad(policytool)

Java tiene en cuenta la seguridad, lo ajustamos de la siguiente forma:

System.setSecurityManager(new RMISecurityManager());

Clase RMISecurityManager

Programador ajusta la seguridad mediante la clase Policy

  • Policy.getPolicy() permite conocer la seguridad actual.
  • Policy.setPolicy() permite fijar nueva política
  • Seguridad reside en fichero específico

Java 2 incorpora una herramienta visual: policytool

Ajuste de la política de seguridad, crearlas con policytool.

Para activarlas:

  • Podemos ejecutar ajustando la política con
java -Djava.security=fichero.policy servidor|cliente
  • Creando un objeto RMISecurityManager y ajustar SetSecurityManager sobre el objeto creado.

Callback del cliente

Si el servidor RMI debe notificar eventos a clientes, la arquitectura es inapropiada. La alternativa estándar es el sondeo (polling), donde cliente:

Iservidor RefRemota = (IServidor) Naming.lookup (URL);
while (!(Ref.Remota.Evento.Esperado())){}

Características

Clientes interesados se registran en un objeto servidor para que les sea notificado un evento, cuando el evento se produce, el objeto servidor notifica al cliente su ocurrencia.

Para esto hay que duplicar la arquitectura, requiriéndose dos interfaces, habrá dos niveles de stubs:

  • Cliente (stub+skel) y servidor (stub+skel)

Es necesario proveer en el servidor medios para que los clientes registren sus peticiones de callback.

Descarga dinámica de clases

Permite cambios libres en el servidor, elimina la necesidad de distribuir (resguardos) si los hay y nuevas clases.

Dinámicamente, el cliente descarga todas las clases que necesita para desarrollar una RMI válida, mediante HTTP o FTP.

Novedades a partir de JDK 1.5

Soporta generación dinámica de resguardos, prscinde del generador rmic s