No es un bug, es una característica no documentada

viernes, 4 de marzo de 2016

Procesos y servicios. RMI (II)

13:06 Posted by Inazio Claver , No comments
(Puedes leer la primera parte aquí)

Introducción a las aplicacione RMI

Las aplicaciones RMi normalmente comprenden dos programas separados: un servidor y un cliente. Una aplicación servidor típica crea un montón de objetos remotos, hace accesibles unas referencias a dichos objetos remotos, y espera a que los clientes llamen a estos métodos u objetos remotos. Una aplicación cliente típica obtiene una referencia remota de uno o más objetos remotos en el servidor y llama a sus métodos.

RMI proporciona el mecanismo por el que se comunican y se pasan información del cliente al servidor y viceversa. Cuando es una aplicación algunas veces nos referimos a ella como Aplicación de Objetos Distribuidos

Objetos distribuidos

Elementos principales:
  • Interfaces remotas
  • Objetos remotos
  • Objetos serializables
  • Stubs
  • Servicio de nombres

Interfaces remotas. Es una interfaz acordada entre el servidor y el cliente. Un método que el cliente puede invocar.
Las clases de los parámetros y del resultado han de ser serializables (en Java simplemente una interfaz) o remotos.

Un objeto se convierte en remoto implementando un interface remoto, que tenga estas características.
Un interface remoto desciende del interface java.rmi.Remote.

Cada método del interface declara que lanza una java.rmi.RemoteException además de cualquier excepción específica de la aplicación.

Objetos remotos. Son objetos cuyos mensajes pueden ser invocados remotamente (desde objetos corriendo en otro proceso. En el caso de Java sería desde otra JVM).

Los objetos remotos deben implementar uno o varios interfaces remotos.

La clase del objeto remoto podría incluir implementaciones de otros interfaces (locales o remotos) y otros métodos (que sólo estarán disponibles localmente). Si alguna clase local va a ser utilizada como parámetro o cómo valor de retorno de alguno de esos métodos, también debe ser implementanda.

Objetos serializables. El RMI utiliza el mecanismo de serialización de objetos para transportar objetos entre máquinas virtuales. Implementar Serializable hace que la clase sea capaz de convertirse en un stream de bytes auto-descriptor que puede ser utilizado para reconstruir una copia exacta del objeto serializado cuando el objeto es leído desde el stream.

Stubs. Actúan como referencias a objetos remotos en el cliente. Es una clase usada por el cliente en sustitución de la remota.

Su clase es generada automáticamente a partir de la interfaz, e implementa la interfaz remota.

La implementación de cada operación envía un mensaje a la máquina virtual que ejecuta el objeto remoto y recibe el resultado, retransmitiendo llamadas desde el cliente hacia el servidor y siendotransparente al código del cliente.

Cuando un cliente invoca una operación remota que devuelve una referencia a un objeto remoto, obtiene una instancia del stub correspondiente.

Servicio de nombres. Permite asociar nombres lógicos a objetos.
El servidor asocia un nombre a un objeto, el cliente obtiene una referencia al objeto a partir del nombre (stub), y así se conseguiría el objetivo, tener transparencia de localización. 

Pasaje de objetos

El intercambio de objetos en la ejecución de un método en un objeto remoto puede ocurrir cuando:

  • Un cliente pasa objetos como parámetro pues así lo requiere el signature del método. El server recibe y puede manipular esos objetos.
  • El servidor reponde a la ejecución de un método a partir de un objeto. El cliente puede utilizar ahora ese objeto que recibe como respuesta.

Cualquiera de estos objetos (parámetros o respuesta) deben ser remotos y si no lo son deben ser serializables.

Si son remotos:
  • Estará tipado con una clase que extiende de UnicastRemoteObject.
  • Se pasan por referencia.
  • Los objetos remotos se convierten en stubs al pasar del servidor al cliente. Si no son objetos que se pueden acceder de manera remota (deben al menos ser serializables):
  • No están tipados con una clase que extienda UnicastRemoteObject (o alguna que indique que es remoto).
  • Deben implementar java.io.Serializable (de lo contrario se produce una excepción).
  • Son pasados por valor.
  • RMI se ocupa de la serialización de forma transparente para el desarrollador.
Ejemplo RMI





Interfaces Java que deriban de la interfaz java.rmi.Remote.


Todos los métodos deben declarar java.rmi.RemoteException.

Argumetnos que pueden tomar los métodos:
  • Tipos primitivos Java
  • Stubs y objetos remotos
  • Objetos locales serializables (implementan la lase java.io.Serializable)

Implementación de Objetos Remotos
  • Subclase de java.rmi.server.UnicastRemoteObject que implementa la interfaz remota.
  • Implementar todos los métodos de la interfaz remota.

Programa Servidor
  • Crea instancias de las clases remotas y las registra en el servidor de nombres

Programa cliente
  • Declara objetos de la interfaz remota y obtiene stubs del servidor de nombres.
  • Invoca métodos sobre los objetos.

Java RMI define un servicio de nombres muy secillo.

El esquema de nombrado sigue la sintaxis de una URL (//maquina:puerto/nombreDeObjeto) siendo:
  • nombreDeObjeto un nombre simple
  • máquina y puerto hacen referencia a la máquina en la que corre el servidor de nombres.

Por defecto, máquina = localhost y puerto = 1099.

Servicio de nombres: rmiregistry


Aplicación que contiene un objeto que implementa el interfaz java.rmi.registry.Registry, no siendo persistente.

Por motivos de seguridad, la implementación de rmiregistry prohíbe que se invoquen los métodos bind, rebind y unbind de su objeto Registry desde otra máquina.

La clase java.rmi.Naming


Ejecución

1.       Arrancar el servidor de nombres (rmiregistry)
2.       Correr la clase servidor
3.       Correr el (los) cliente(s)
4.       En el momento de la ejecución, el cliente debe disponer en su máquina de:
a.       .class de cada interfaz remota
b.      .class de cada clase stub correspondiente

Ejemplo RMI. Código


Interfaz

package rmi_sample;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface StringerInterface extends Remote{

      String getString() throws RemoteException;
}

Servidor

package rmi_sample;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class Stringer extends UnicastRemoteObject implements StringerInterface{

      private String str = "DEFAULT";
     
      public Stringer(String s) throws RemoteException{
            super();
            str = s;
      }
     
      public String getString() throws RemoteException{
            return str;
      }
     
      public static void main(String[] args) {
            String name;
            StringerInterface robject;
           
            if(System.getSecurityManager() == null){
                  System.setSecurityManager(new RMISecurityManager());
            }
           
            name = "//localhost/StringerInterface";
            try{
                  robject = new Stringer("Hi\n");
                  Naming.rebind(name, robject);
                  System.out.println("Stringer bounded");
            }
            catch(Exception e){
                  System.err.println("*******ComputeEngine exception: *******");
                  e.getMessage();
            }
      }
}

Cliente

package rmi_sample;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class StrClient {

      public static void main(String[] args) {
            if (args.length < 1){
                  System.out.println("Necesita el hostname");
                  System.exit(0);
            }
           
            if (System.getSecurityManager() == null){
                  System.setSecurityManager(new RMISecurityManager());
            }
           
            try{
                  String name = "//" + args[0] + "/StringerInterface";
                  StringerInterface robject = (StringerInterface)Naming.lookup(name);
                  String result = robject.getString();
                  System.out.println(result);
            }
            catch(Exception e){
                  System.err.println("******** Problemas en la invocación ********" + e.getMessage());
            }
      }
}

Conclusión

RMI es un sistema que nos permite el intercambio de objetos, el cual se realiza de manera transparente de un espacio de direcciones a otro, puesto que utiliza una técnica de serialización. Además nos pemite el llamado de los métodos remotamente, sin tener la necesidad de tener los métodos localmente.

Debido a que los sistemas necesitan manejo de datos en sistemas distribuidos cuando estos residen en direcciones de distintos hosts, los métodos de invocación remota son una buena alternativa para solucionar estas necesidades. RMI es un sistema de programación para la distribución e intercambio de datos entre distintas aplicaciones existentes en un entorno distribuido.

Se debe tener en cuenta que es más lento porque los objetos tienen que serializarse y luego deserializarse, se debe chequear la seguridad, los paquetes tienen que ser ruteados a través de switches.

Esto trae como conclusión que, lamentablemente, el diseño de un sistema distribuido, no es solamente tomar un conjunto de objetos y ponerlos en otro proceso para balancear la carga.

0 comentarios:

Publicar un comentario