Buscar
Social
Ofertas laborales ES
« Seminario de Apache Solr | Main | Grabación del webinar sobre Spring Core »
jueves
jun032010

Hay esperanza para los frameworks AJAX céntricos en el servidor en Google App Engine

Google App Engine (GAE) es un muy atractivo hosting Java para aplicaciones web, pues combina buen precio (el primer nivel es gratuito), facilidad de despliegue, de gestión e "inifita" escalabilidad. 

Sin embargo tiene muchas limitaciones tal y como un gran número de clases Java prohibidas, multihilo limitado (no nuevos hilos), una única tecnología de base de datos etc.

Hay otra muy importante limitación, sólo soporta sesiones replicadas, es decir, "sticky sessions" (o "afinidad de sesión") no funciona en GAE 

En teoría en GAE cualquier petición web puede ser redirigida a cualquier nodo del cluster (computador o instancia JVM para ser exactos) en donde tu aplicación se está ejecutando, GAE es responsable de sincronizar automáticamente los datos en la sesión entre nodos. Como te puedes imaginar GAE serializa los datos cambiados en la sesión en la última petición para ser compartidos entre nodos a través del uso de memcache y el almacenamiento persistente de GAE. Cuando un nodo diferente es usado para recibir peticiones, los datos de la sesión pueden ser leídos en dicho nodo, GAE de-serializa dichos datos reconstruyendo los objetos Java en dicho nodo. Debido a que el mecanismo de serialización es usado los objetos Java guardados en la sesión deben ser serializables.

A través del uso de sesiones replicadas, GAE proporciona "escalabilidad elástica", cuando tu aplicación está siendo demasiado usada GAE puede automáticamente añadir más nodos para tu aplicacióny los usuarios actuales pueden hacer uso de estos nuevos nodos automáticamente pues no están vinculados a un nodo concreto.

Sin embargo la escalabilidad elástica tiene un precio debido a que la serialización y de-serialización consumen tiempo al igual que el transporte de dichos datos entre nodos ocupando además ancho de banda de la red interna de Google. Cuando hay muchos datos en la sesión la escalabilidad elástica puede resultar en una penalización importante de rendimiento si hay un cambio frecuente de nodos sirviendo al mismo usuario.

Para mitigar este problema GAE no es tan elástico, es decir, la mayoría de las peticiones de un mismo usuario son enviadas, afortunadamente, al mismo nodo. Cuando no se está usando el sitioweb durante un tiempo (algunos minutos) y se envían nuevas peticiones, estas nuevas peticiones normalmente son enviadas a un diferente nodo, se "siente" este cambio de nodo porque la aplicación sufre uns significativa parada.

Hace un año el misterioso blogger “StringBuffer” hizo un experimento para entender como GAE distribuye las peticiones entre nodos, los resultados mostraron que GAE casi nunca cambia las peticiones de un mismo usuario entre nodos cuando estas peticiones son muy seguidas. A pesar de que el experimento no representa el comportamiento de un usuario real (una petición por segundo es un lapso de tiempo muy pequeño), es  muy significativo para "demostrar" que en el mundo real, GAE funciona la mayor parte del tiempo en modo "sticky sessions" porque en una aplicación web (un sitio web podría ser diferente) los usuarios ejecutan peticiones dentro de intervalos pequeños de tiempo.

Este patrón de comportamiento de GAE abre una nueva esperanza para los frameworks céntricos en el servidor intensivos en AJAX, este tipo de frameworks almacenan y gestionan el estado de la vista en principalmente en el servidor. Cualquier enfoque tiene sus pros y sus contras, obviamente la gestión de estado en el servidor consume más memoria y algo más de CPU del servidor para la lógica de vista, pero hay importantes ventajas tal y como mayor seguridad, menor carga del cliente (incluyendo transferencia de lógica al mismo), mayor velocidad si la lógica de vista y/o de negocio es compleja (Java es de lejos más rápido que JavaScript), más sencilla la codificación sin duplicación de código y necesidad de puentes cliente/servidor a medida (porque datos y vista comparten el mismo espacio de memoria) y la bondad de la codificación en Java (madurez, riqueza de librerías, tipado estático, soporte de IDEs, OOP fuerte etc).

Los frameworks de tipo servidor funcionan mejor en modo sticky sessions porque en este modo la serialización no es oligatoria y si la aplicación web es serializable la serialización apenas ocurre en caso de fallo del nodo o de recarga del servidor de servlets.

Como es conocido, en general los frameworks céntricos en el servidor intensivos en AJAX promueven las aplicaciones Single Page Interface (o con un pequeño número de páginas), estas aplicaciones son muy "stateful" desde el punto de vista de la vista. En GAE la serialización es obligatoria para los datos guardados en sesión,  cuando hay mucho estado en el servidor la serialización y el cambio de nodos puede afectar seriamente al rendimiento. Una simple petición AJAX que modifica ligeramente la vista implica que la vista ha cambiado, para notificar a GAE de este cambio, el atributo sesión que guarda el estado de la vista debe ser actualizado para que GAE detecte este cambio y serialice de nuevo la vista completamente. Si la petición siguiente implica un cambio de nodo el coste es incluso mayor debido al transporte de datos y de-serialización. Por supuesto algun tipo de particionado de la vista en la sesión y estrategias de deltas son posibles pero pueden ser muy complejas de llevar a cabo.

Hay una alternativa en GAE, las aplicaciones híbridas, que son al mismo tiempo "stateful" (con estado en el servidor) y "stateless" (sin estado).

La idea es sencilla: NO GUARDAR EL ESTADO DE LA VISTA EN LA SESIÓN, guardar únicamente los datos del usuario para ser compartidos entre nodos (normalmente datos no persistentes y cacheados). Esto puede sonar a una locura pues estamos hablando de aplicaciones céntricas en el servidor en la gestión de la vista e intensivas en AJAX, pues cuando GAE envíe un evento AJAX a un nodo diferente, no habrá allí información sobre la vista guardada en la sesión (sólo los datos del usuario en la sesión).

¿Y si reconstruimos el estado de la vista en el nuevo nodo?

Esta es la clave de esta aparentemente absurda propuesta, si nuestro evento AJAX contiene la suficiente información para reconstruir el estado de la vista en el servidor, podemos evitar la problemática de la serialización, el transporte entre nodos y la deserialización. La más sencilla técnica es la de recargar de nuevo la página del cliente en el nuevo estado esperado cuando el evento AJAX fue enviado. Si podemos reconstruir el estado del servidor en el nuevo nodo con la información del estado que tenemos en el cliente, nuestra aplicación anteriormente "stateful" es ahora "stateless" desde el punto de vista de la vista.

Las aplicaciones híbridas pueden ser reales ya mismo.

Seguramente es conocido mi experimento de portar (parcialmente) la web de El Corte Inglés a SPI, la demo online se ejecuta un hosting convencional Java de un único nodo. El siguiente reto es  ¿puede funcionar también en GAE?

La capacidad de funcionar con JavaScript desactivado es la clave, puesto que en las URLs tenemos la suficiente información para poder recrear la vista en un estado dado (por supuesto contando también con el modelo de datos a representar en el servidor).

ItsNat soporta GAE y por tanto sesiones replicadas llamando a  ItsNatServletContext.setSessionReplicationCapable(boolean) con un valor true, en este modo cualquier petición recibida implica que el atributo de sesión con la vista es actualizadoy por tanto la vista podría ser serializada en cada request.

Pero si "SessionReplicationCapable" lo ponemos a falso (por defecto), ItsNat no salva el estado de la vista en la sesión, en este caso la sesión es apenas usada por ItsNat para temas de seguridad (identificación del navegador cliente) y prácticamente vacía.

A pesar de las técnicas de cacheado DOM de ItsNat para liberar memoria en las zonas estáticas del sitio web (guardadas como markup plano compartido entre todos los usuarios), en nuestro ejemplo la vista es lo suficientemente compleja y pesada y puede suponer un problema de rendimiento la serialización/transporte/deserialización. Por tanto decidimos NO activar el modo SessionReplicationCapable también en el caso de GAE.

De esta manera cuando un evento AJAX es enviado a un nodo diferente, no hay estado de la vista en la sesión, siguiendo terminología ItsNat el  ItsNatDocument que contiene el árbol DOM de la vista no está en dicho nodo (ItsNat usa internamente un valor único para reconocer cuando el documento del cliente se corresponde con alguno en el servidor).

ItsNat ofrece una posibilidad de procesar y reconocer este evento "huérfano", si un EventListener global fue registrado por el programador, el evento AJAX lo reconocemos como huérfano porque devolverá null en la llamada ItsNatEvent.getItsNatDocument(). En este caso obtendremos del evento el nuevo estado deseado como retorno cambiaremos el URL del cliente al nuevo estado y ordenaremos via JavaScript una recarga de la página, dicha recarga será procesada por el nuevo nodo que reconstruirá la vista en el estado esperado.

En resumen, nuestro sitio web en GAE funcionará como SPI cuando el usuario envíe peticiones suficientemente seguidas, cuando no se envíe una petición durante unos pocos minutos, la siguiente petición será normalmente asignada por GAE a otro nodo, este nodo detectará este evento AJAX como huérfano y recargará la página en el esperado estado, los siguientes eventos si sono seguidos normalmente será enviados a este nodo y la aplicación volverá a ser de nuevo SPI. Aunque si JavaScript está desactivado la aplicación funcionará totalmente como basada en páginas. 

Enlace a la aplicación demo en GAE 

Notas: 

1)   Este ejemplo se ejecuta en el nivel gratuito de GAE, la calidad de servicio en este nivel no es la mejor, por ejemplo cuando un nuevo nodo es requerido la carga de la aplicación web lleva cierto tiempo, si la aplicación está ya ejecutándose en ese nodo (por ejemplo si otros usuarios están usando la aplicación) el tiempo debería ser menor.

2)   Debido a que el ejemplo es un clon parcial muchos enlaces apuntan a la web original. Un alert muestra cuando estás saliendo de la página o recargando. Recomiendo familiarizarse con el ejemplo en el hosting no GAE para conocer bien cual es la parte clonada SPI. En la versión GAE ir a dicha parte y esperar dos o tres minutos, entonces GAE enviará el siguiente evento a nuevo nodo y la página se recargará (este es el comportamiento esperado de acuerdo con este artículo), un alert se mostrará indicando ésto (que se abandona la página), obviamente este "alert" no estará presente en una aplicación normal.

3)    La versión actual de ItsNat (0.7.0.6) tiene un fallo menor con la gestión de las instancias de las sesiones (solucionado en versiones futuras), en GAE la instancia de la sesión puede cambiar entre peticiones, por ello en GAE (en modo de no replicación de sesiones) el objeto HttpSession debe obtenerse de la forma normal desde HttpServletRequest.getSession().

4)    El control remoto no funciona correctamente en GAE

¿Hay futuro para los frameworks céntricos en el servidor intensivos en AJAX en Google App Engine?

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.
Comentarios deshabilitados
Comentarios deshabilitados en esta noticia.