introducción - cediarepositorio.cedia.org.ec/bitstream/123456789/966/13/curso_2014_3.… · para...

15
Introducción En esta nueva práctica vamos a explorar el concepto de “modo de vida” de un objeto remoto. Este concepto es fundamental para entender el funcionamiento del mismo y es necesario para muchas de las decisiones de diseño y arquitectura de una aplicación distribuida. Existen varios modos de vida de los objetos remotos y cada middleware suele ofrecer unos u otros pero todos ellos suelen ser variantes más o menos similares. Este concepto no debemos confundirlo con el de “ciclo de vida” que también abordaremos a lo largo de las prácticas. Curso de Middleware. Práctica 3. 1 de 15

Upload: others

Post on 21-Aug-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

IntroducciónEn esta nueva práctica vamos a explorar el concepto de “modo de vida” de un objeto remoto. Este concepto es fundamental para entender el funcionamiento del mismo y es necesario para muchas de las decisiones de diseño y arquitectura de una aplicación distribuida.

Existen varios modos de vida de los objetos remotos y cada middleware suele ofrecer unos u otros pero todos ellos suelen ser variantes más o menos similares. Este concepto no debemos confundirlo con el de “ciclo de vida” que también abordaremos a lo largo de las prácticas.

Curso de Middleware. Práctica 3. 1 de 15

Page 2: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Jugando con el Objeto RemotoPara seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que ya tenemos desarrollado y sigamos trabajando sobre el código de nuestra práctica anterior.

Sobre el código anterior vamos a añadir un poco de código a nuestra calculadora para que nos permita guardar y recuperar un número. El cambio es rápido. Bastaría con añadir el siguiente código a la calculadora:

public class Calculadora : System.MarshalByRefObject

{

private int dato = -1;

public void GuardarDato(int d)

{

dato = d;

}

public int ObtenerDato()

{

return dato;

}

public int Dato

{

get { return dato; }

set { dato = value; }

}

.. resto del código..

}

Además de los métodos para guardar o recuperar la información (“GuardarDatos” y “ObtenerDatos”), hemos utilizado un atributo de C# (“Dato”). Un atributo en C# hace la misma función que los get/set de Java. Permite recubrir una variable para controlar el acceso. También permite disfrazar de variable una función. Es un concepto muy potente y merece la pena conocerlo. Consulta tu manual de C# al respecto.

Y ahora añadimos algunas llamadas a estos métodos desde nuestro cliente. Por ejemplo, os propongo algo del estilo:

Curso de Middleware. Práctica 3. 2 de 15

Page 3: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

public static void Main(string[] args)

{

// código antiguo ...

Console.WriteLine("Creando la calculadora");

Calculadora calc = new Calculadora();

Console.WriteLine("El valor del dato al principio =" +

calc.ObtenerDato());

Console.WriteLine("Voy a guardar 10 en el dato.");

calc.GuardarDato(10);

Console.WriteLine("Ahora, el valor del dato es =" +

calc.ObtenerDato());

// el resto del código antiguo iría aquí

}

No parece muy complicado, verdad? Realmente lo único que hacemos es preguntar por el valor de una variable, la cambiamos posteriormente y volvemos a comprobar que el valor se ha almacenado correctamente. Seguramente hayamos hecho cosas parecidas infinidad de veces.

¡Es importante que lo pruebes!. No sigas hasta haber conseguido esta parte!.

Prueba primero en una arquitectura no distribuida y seguidamente vuelve a probar con el ejemplo cliente/servidor. Recuerda que si comentas la línea en la que configuras Remoting del cliente, pasas a modo no distribuido. En mi caso, he obtenido lo siguiente cuando la aplicación era local:

Curso de Middleware. Práctica 3. 3 de 15

Page 4: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Ahora volvemos a descomentar la línea de configuración de Remoting en el cliente para volver a tener un funcionamiento distribuido. En caso, yo he obtenido lo siguiente:

¿Has obtenido el resultado que esperabas? Seguramente no, verdad?.

Si observamos la traza generada, el sistema nos dice que el valor del dato después de haberlo cambiado por 10 …. !Sigue siendo -1!!!.

¿Cómo es posible que suceda esto?. Debería valer 10, no?

Vamos a descubrir qué está pasado y las razones que hay detrás de este comportamiento tan confuso. Para ello intenta lo siguiente.

Define un constructor para la calculadora (no te olvides que debe ser público) y pon una traza indicando que has pasado por allí. Por ejemplo, prueba con algo similar a:

...

public Calculadora()

{

Console.WriteLine("Estoy en el constructor de la

Calculadora");

}

...

Ahora, vuelve a comparar el funcionamiento entre la aplicación local y la aplicación distribuida. ¿Obtenemos los mismos resultados?.

En mi ejemplo, yo he obtenido con la aplicación local (comentando la línea de configuración) lo siguiente:

Curso de Middleware. Práctica 3. 4 de 15

Page 5: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

y con la aplicación en modo distribuida:

Si has realizado los mismos pasos, podrás observar que en el servidor se llama al constructor de la calculadora TRES VECES!!. Una vez por cada cada llamada a los métodos. Pero lo más asombroso es que no hay NINGUNA llamada al constructor en el cliente. En cambio, el cliente hace un “new Calculadora()”, por lo que deberíamos esperar al menos una traza generada por el constructor.

Para estar seguros, os propongo que definamos una variable estática que se incremente en el constructor. Ese valor lo podemos guardar como identificador de la calculadora.

Imprime en la trazas del constructor y de los métodos ese identificador. Pregunta si

Curso de Middleware. Práctica 3. 5 de 15

Page 6: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

tienes dudas de como funciona una variable estática (también se denomina variable de clase). El código que os propongo sería similar a éste:

...

public class Calculadora : System.MarshalByRefObject

{

private static int contador = 0;

private readonly int miID;

private int dato = -1;

public Calculadora()

{

miID = contador++;

Console.WriteLine("Estoy en el constructor de la

Calculadora. Mi Id =" + miID);

}

public void GuardarDato(int d)

{

Console.WriteLine("Estoy GuardarDato. Mi Id =" + miID);

dato = d;

}

public int ObtenerDato()

{

Console.WriteLine("Estoy ObtenerDato. Mi Id =" + miID);

return dato;

}

...

De nuevo, comparamos el funcionamiento tanto en local como en remoto. En local veremos algo de este estilo:

Curso de Middleware. Práctica 3. 6 de 15

Page 7: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Efectivamente, cuando hemos ejecutado la aplicación en local, hemos visto que solo se construía una calculadora y que ese ID era el mismo para todas las llamadas.

Ahora lo comprobamos en distribuido, y obtenemos algo similar a:

En este caso, vemos que cada llamada se ejecuta en una calculadora diferente (se observan diferentes ID en las trazas).

Ejecuta varios clientes y varias veces con un mismo servidor (sin pararlo). Intenta explicar el funcionamiento antes de continuar.

Curso de Middleware. Práctica 3. 7 de 15

Page 8: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

La razón de que cada llamada cree un objeto remoto se debe a que hemos dicho en nuestro fichero de configuración que el servicio era “ SingleCall”. Compruébalo en el fichero de “App.config” del proyecto servidor.

...

<wellknown

mode="SingleCall"

type="Calculo.Calculadora, Calculo"

objectUri="Calculadora.remota"

/>

...

Este parámetro le indica al sistema que el objeto debe ser creado en cada llamada. El sistema destruye el objeto al terminar la llamada. Suele ser útil cuando no interesa mantener información entre llamadas.

La alternativa sería “Singleton” que indica que todas las llamadas se refieren al mismo objeto. La vida del objeto (cuando se crea o hasta cuando existe) se define combinando varias características y ésta es una de ellas. Probad a cambiar el modo de activación y comprobad que las trazas se adaptan a ese nuevo modo.

...

<wellknown

mode="Singleton"

type="Calculo.Calculadora, Calculo"

objectUri="Calculadora.remota"

/>

...

Os adjunto de nuevo las trazas que he obtenido al cambiar la configuración por “Singleton”. Dado que esta configuración solo se aplica a la configuración del servidor, no tiene sentido volver a probar en modo local.

Curso de Middleware. Práctica 3. 8 de 15

Page 9: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Ahora parece que nuestro funcionamiento es el esperado. Verdad? ¿Realmente? ¿O todavía tenemos alguna sorpresa más?

Si volvemos a lanzar un cliente (sin parar el servidor) veremos que el nuevo cliente utiliza … la misma calculadora que el cliente anterior! Si lanzamos varios clientes simultáneos, TODOS ellos están utilizando la misma calculadora. En una ejecución con cuatro clientes yo he obtenido lo siguiente:

Quizás debamos reflexionar sobre lo que está sucediendo. Las cosas no parecen tan evidentes cómo creíamos.

Lo que está sucediendo es que los objetos remotos “toman vida” de forma

Curso de Middleware. Práctica 3. 9 de 15

Page 10: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

independiente a la vida que se observa localmente. La vida de nuestro objeto local nace cuando hacíamos “Calculadora calc = new Calculadora();” y moría cuando se cerraba su ámbito.

En cambio el objeto remoto puede tener modos de vida muy diferentes. Y es muy común asociar estos modos de vida al estado que deseamos guardar en el servidor. Aunque en .Net Remoting tengan los nombres de “SingleCall” o “Singleton”, en otros middleware pueden tener otros nombres. Por ejemplo en JavaEE aparecen los nombres “Stateful” y “Stateless”. Su significado es ligeramente diferente al que hemos visto, pero reflejan el mismo concepto de que los objetos remotos tienen “vida propia”. La totalidad de los middleware basados en objetos remotos tienen esta forma de trabajar. Algunos de ellos son muy elaborados, otros muy sencillos y la gran mayoría ofrecen alternativas para simular cualquier modo de vida que necesitemos.

Lo más complejo de esta práctica es aceptar que los objetos remotos tienen una vida independiente del código del cliente. Es decir, los objetos remotos son construidos y destruidos por el servidor que los aloja, siguiendo diferentes políticas. Estas políticas definen cuándo un objeto debe ser construido, compartido y destruido.

Y que este funcionamiento tan extraño tiene sentido. Mucho sentido!.

Es un buen momento para que tu profesor os cuente conceptos de teoría. No avances en la práctica hasta asegurarte que has entendido correctamente los objetivos de la misma!!!!.

Hemos visto que el concepto de modo de vida y dos alternativas de .Net Remoting (singlecall y singleton). Este concepto es importante y para ello te ofrecemos algunos puntos de reflexión:

• Cuando hemos puesto trazas en el constructor, no se observan en la pantalla del cliente. ¿Es eso posible?

• Lo más revelador de la práctica es que los objetos remotos tienen una vida diferente a los objetos locales. Pero la programación del cliente utiliza un objeto local de calculadora. ¿Sabrías decir que problemas pueden surgir?

• En la práctica anterior, parece que el modo “SingleCall” es poco útil. Pero en realidad es bastante utilizado. ¿Sabrías decir dónde?. ¿Se te ocurren ventajas e inconvenientes de este modo?

• Igualmente, piensa sobre el modo “Singleton”. Razona sobre sus ventajas e inconvenientes.

• Intenta encontrar algún ejemplo en el que utilizarías uno u otro modo. Razona tu respuestas.

• ¿Se te ocurren alternativas a los modos “SingleCall” y “Singleton”?

• Al cambiar los parámetros de configuración, solo hemos tocado el fichero de configuración del servidor. El cliente no ha sido modificado. ¿Sabe el cliente qué modo de vida tiene el objeto calculadora?

Curso de Middleware. Práctica 3. 10 de 15

Page 11: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

• Normalmente, los programadores de las aplicaciones cliente y los programadores del servidor suelen ser diferentes. El modo de vida lo han elegido los programadores del servidor. ¿Cómo saben los clientes el modo de vida que tiene los objetos de un servidor? ¿Podrían controlar los clientes el modo de vida de un objeto remoto?. ¿Tiene sentido tal necesidad?

Curso de Middleware. Práctica 3. 11 de 15

Page 12: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Ejercicios adicionales

Como viene siendo habitual, vamos a vamos a realizar algunos cambios que nos ayuden a comprender otros conceptos relevantes. Nuestro objetivo ahora es explorar las consecuencias que nos implica el modo de vida y el impacto en el diseño del lenguaje. Os propongo los siguientes ejercicios adionales:

1. Seguramente os hayáis dado cuenta que nuestros antiguos métodos de sumar y multiplicar eran en realidad candidatos a métodos de clase (“static”). Es decir, no dependen del estado del objeto para ejecutarse. Si cambiamos esos métodos de instancia a métodos de clase (les ponemos “static”), ¿El funcionamiento de nuestro aplicativo cambiaría mucho? ¿Se vería afectado por la declaración de un modo ¿SingleCall o Singleton?.

Antes de hacer los cambios os propongo que pensemos y analicemos lo que podríamos esperar.

Ahora vamos a hacer los cambios. Os propongo el siguiente cambio:

public static double Suma(double izq, double dch)

{

Console.WriteLine("Estoy en la suma de doubles, sumando {0} +

{1}", izq, dch);

return izq + dch;

}

Si ahora ejecutamos nuestro cliente. ¿Qué ha pasado? ¿Dónde aparecen las trazas? ¿Y si cambiamos el modo de vida? ¿Hemos acertado en nuestras predicciones?

2. Retomemos nuestro código anterior en el que guardábamos e imprimíamos el número de ID de nuestro objeto. Os propongo algo de este estilo:

public static double Suma(double izq, double dch)

{

Console.WriteLine("Estoy en la suma de doubles, sumando {0} +

{1}", izq, dch);

Console.WriteLine("Mi Id es =" + miID);

return izq + dch;

}

¿Nuestro código compilaría? Seguramente no podamos compilar este código. ¿Sabríamos explicar la razón? ¿Crees que tiene alguna relación con lo que nos ha sucedido en el ejercicio anterior?

3. ¿Este comportamiento sucede solo con los métodos estáticos? ¿Podría suceder algo parecido con los datos estáticos?. Una forma de comprobarlo es hacer métodos de instancia que modifiquen y retornen datos de clase.

Curso de Middleware. Práctica 3. 12 de 15

Page 13: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Trabajad en grupo y proponer un método de comprobación. Cuando hayáis terminado, comentad con el profesor vuestras sugerencias y ponedlo en práctica.

Cuando terminéis, comentad vuestros resultados con el resto de los asistentes.

Curso de Middleware. Práctica 3. 13 de 15

Page 14: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

ResumenA lo largo de esta práctica hemos aprendido que los objetos remotos tienen un modo de vida. Dado que el middleware hace tan trasparente la programación distribuida, si no entendemos ese concepto nuestro código puede ser propenso a errores. Hemos visto únicamente dos modos de vida, pero en la práctica nos encontraremos con algunos más. En la siguiente práctica vamos a explorar algunos más.

Curso de Middleware. Práctica 3. 14 de 15

Page 15: Introducción - CEDIArepositorio.cedia.org.ec/bitstream/123456789/966/13/Curso_2014_3.… · Para seguir con nuestro trabajo, os recomiendo que hagáis copia de seguridad de lo que

Conceptos introducidosEn esta práctica hemos introducido/repasado los siguientes conceptos:

• Modo de vida de un objeto remoto• Modo SingleCall• Modo Singleton• Miembros y métodos de clase y de instancia

Curso de Middleware. Práctica 3. 15 de 15