lunes, junio 11, 2007

Comet con Tomcat 6, Jetty y Grizzly

lunes, junio 11, 2007 por Martín

Hace unas semanas comentaba que estaba evaluando proyectos comerciales y Open Source para implementar un sistema basado en Comet. Parte de ese trabajo fue evaluar las implementaciones de Tomcat 6 y Jetty. De esa evaluación extraje algunas ideas que paso a resumir por aquí, por si a alguien le hace falta.

Jetty

Jetty se basa en el soporte de continuations (ver Continuation en general), que simplemente (o no tan simplemente) consiste en aparcar el procesado de una request en concreto para poder hacer disponible el Thread que la estaba tratando para otras requests.

La principal característica de la implementación de Jetty es que se sigue construyendo sobre el modelo tradicional de Servlets, con la única diferencia de que existe un API adicional que te permite aparcar una request en concreto. Para eso creas un objeto Continuation sobre la request. También se puede asociar estado con esa Continuation:
protected void doGet(HttpServletRequestt request, 
HttpServletResponse response) ... {
....
Continuation continuation = 
ContinuationSupport.getContinuation(request,this);
continuation.setObject(new CometRateSender());
}

Una vez que has trabajado con la Continuation puedes aparcarla por un rato:
protected void doGet(HttpServletRequestt request, 
HttpServletResponse response) ... {

.... code above ....
continuation.suspend(100).
}

¿Cuál es el truco? Pues bien, resulta que después de llamar a suspend, Jetty lanza una excepción concreta que el motor de Servlets capturará. Una vez capturada, Jetty almacena esa request y pasa a utilizar el Thread liberado para manejar otras peticiones. Cuando ha pasado el timeout, Jetty recupera la request "suspendida" y vuelve a ejecutar el método doGet/doPost de nuevo.

Como veis se trata de una triquiñuela inteligente que no a todos gustará ya que se basa en lanzar excepciones internamente y rellamara a los métodos get y post continuamente lo que te obliga a realizar un proceso un tanto extraño de las peticiones. El caso es que para ser honestos, el rendimiento es bueno. Con una configuración normalita (Sun Fire V240, 2 Cores y Solaris 8), y ejecutando un test bastante exigente (streaming continuo y constante en tiempo real), fue capaz de mantener de 2000 a 3000 conexiones concurrentes (y aquí concurrente significa enviar continuamente datos al cliente, nada de dormir) manteniendo un 80/90% de la CPU ocupada y rondando unos 500Mb de consumo de memoria.

Tomcat 6

Desde el lanzamiento de la versión 6 (ahora la versión estable de este producto) Tomcat soporta también el modelo Comet, aunque de una forma bastante diferente a Jetty.

El modelo de Tomcat no es un maquillaje sobre la especificación de Servlets, sino que han evitado cualquier tipo de integración y han creado un API propia de gestión de eventos. Esto tiene la desventaja de obligarte a programar de un modo diferente pero por otra parte ofrece un mayor control sobre el modelo de comunicación de tu aplicación web.

En Tomcat 6, un Servlet que quiera realizar Comet tiene que implementar una interfaz concreta, CometProcessor. Cuando Tomcat detecta una llamada a un Servlet que implementa esta interfaz, en lugar de llamar a los métodos doGet y doPost llamará al método especial event:
public void event(CometEvent ce) 
throws IOException, ServletException {

HttpServletRequest request = ce.getHttpServletRequest();
HttpServletResponse response = ce.getHttpServletResponse();
....
}

Tomcat define varios tipos de eventos que se pueden tratar dentro de este método. Así tenemos BEGIN, READ o END, por ejemplo, que marcan el ciclo de vida de la aplicación Comet:
public void event(CometEvent ce) 
throws IOException, ServletException {

... code above ...
if (ce.getEventType() == CometEvent.EventType.BEGIN) {
// store the response somewhere

} else if (ce.getEventType() == CometEvent.EventType.ERROR) {
... get response ...
response.getOutputStream().close();
ce.close();
} else if (ce.getEventType() == CometEvent.EventType.READ) {
.. read bytes and handle them ...
}
}

El método READ se puede utilizar para realizar una implementación basada en polling. De otro modo, la idea es cachear los objetos response y periódicamente utilizarlos para enviar las notificaciones. La mala noticia es que todo el modelo de programación multitarea y la sincronización quedan en manos del desarrollador. Añade algo de complejidad pero tampoco es tan complicado.

Tras los tests realizados, los resultados en cuando a clientes soportados son más o menos los mismos que Jetty, aunque en Tomcat llegué a los 4000 usuarios concurrentes, pero no sería justo decir que soporta más ya que con Tomcat dediqué mucho más tiempo. Todo esto a todo trapo, con la CPU a 80/90%. Sin embargo en Tomcat la memoria utilizada es mucho mayor llegando a 1Gb, es decir el doble. Este consumo de memoria se debe a que yo sí que cacheé los objetos response, pero sinceramente era sólo una prueba, y puede que haya mejores formas de hacerlo.

Grizzly

La verdad es que Grizzly no lo he evaluado. Pero lo he incluido aquí simplemente para enlazar un par de artículos (este y este) que he visto en el blog de lasterra y que seguro que a más de a uno le interesan.

¿Por qué no lo he evaluado? Pues porque es un producto demasiado nuevo, muy difícil de meter en bancos (donde Tomcat está muy enraizado), y porque a uno deja de darle mala espina el ver que toda la documentación de un producto se encuentra únicamente en un blog en concreto, el de su autor. Casi podríamos invertarnos un término nuevo: "Single point of documentation". La falta de referencias importantes (salvo Sun) también le echa a uno atrás la verdad. En fin, además es que lees las mailing lists, foros y eso, y no deja de parecerte un proyecto "inflado", por decirlo de algún modo; porque en las presentaciones no dejan de hablar de sus múltiples hazañas, pero después vas a los foros y listas y nada de nada, ni referencias, ni cientos de mensajes a mayores de los del propio autor.

Personalmente encuentro que Tomcat es una apuesta segura. Fácil de vender, sobradamente probado, con muchas empresas que dan soporte, y conocido en todas partes. Jetty me ha parecido mejor, pero la difernecia no es tan grande como para superar el resto de ventajas de Tomcat.

comments

6 Respuestas a "Comet con Tomcat 6, Jetty y Grizzly"
lasterra dijo...
11:40

Buenos, si es cierto que glassfish no tiene muchos usuarios detrás, pero en su defensa hago los siguientes comentarios:
1.- Grizzly hasta hace bien poco no era más que el conector HTTP de glassfish. Es algo muchas veces abstracto para el programador.
2.- Grizzly ha sido revautizado para glassfish, pero "existe" desde Sun App Server 8.x
3.- Grizzly nos habré la puerta a la capa HTTP dentro de nuestras aplicaciones, y creo q es lo que intenta explicar Jean-Francois en su blog. Creo que pocos programadores tienen interés en esto.
4.- Recientemente grizzly se ha independizado, y ahora hay que ver el éxito que tiene como producto externo a glassfish, como servidor web, servidor comet o servidor JRuby.
5.- Me parece que la implementación de comet es bastante elegante.

Me sorprende y me parece interesante un comentario que haces sobre el tomcat y los bancos. Desconozco el sector, pero si me sorprende que prefieran tomcat frente a productos con quizás mejor soporte como un servidor comercial o de una compañía como Sun Microsystem


Martín dijo...
12:09

Bueno, una de las cosas que he descubierto aquí es que Tomcat es ubicuo en los bancos (aunque bueno, los bancos es que tienen de todo).

Ningún administrador te va a poner ninguna pega cuando le dices que tu producto lleva Tomcat, o requiere Tomcat, porque es algo que ya conocen de hace muchos años, y en muchos casos saben perfectamente como tunearlo, administrarlo, etc. Es... como Apache.

Lo que si que he visto es algún banco que te obliga a poner Apache delante, no por rendimiento, si no por seguridad y porque ya tienen implementada toda la capa de seguridad y sesiones sobre Apache, lo que te plantea muchos retos (por lo de pronto te rompe Comet).

En mi experiencia, cualquier otro producto sin tanto calado, léase Jetty/Grizzly/..., es un ente extraño, y como tal crea mucha susceptibilidad, sea del tipo que sea y venga de la compañía que venga (implica buscar referencias, analizar bugs, ver agujeros de seguridad, aprender a administrarlo, chequear compatibilidad con otros productos, bla bla bla).


Martín dijo...
12:10

Se me olvidaba dejar la coletilla. Aquí Tomcat se considera un "easy-selling".


andretti dijo...
3:39

Hola Martin, creo que asi te llamas...

mira, la razon por el cual hago este comentario es simplemente para pedirte ayuda en cuanto a la implementacion de esta tecnologia con el CometProcessor de Tomcat...

Estoy tratando de realizar una aplicacion sencilla el cual me muestre la hora actual del sistema en el navegador...pero... no muestra nada hasta que se termina el tiempo otorgado en el setTimeout...luego de este tiempo... me muestra una chorrera de lineas con la hora del sistema... siendo estos pues los resultados no esperados... espero me ayudes... te lo agradezco de antemano... ya llevo como 1 año tratando de aplicar esta tecnologia debido a que no conocia nada de tomcat, servlets y todo eso....

espero me des una respuesta, gracias de antemano de nuevo...


Martín dijo...
10:01

No te puedo contestar, porque eso ya hace demasiado que lo he tocado. Seguro que hasta ha cambiado.

Te recomiendo (http://tomcat.apache.org/tomcat-6.0-doc/aio.html). En su momento lo probé y funcionaba bien, si no te funciona quizás estés haciendo algo mal, en cuyo caso deberías dejar un mensaje en la lista de correo de Tomcat.


andretti dijo...
21:06

ok... muchas gracias...

mi problema radicaba en que mal interpretaba la tecnologia comet como tal... pensaba que solamente consistia en que se mantenia la conexion abierta entre cliente y servidor... pero me he dado cuenta de que comet usa diversas tecnicas tales como el long polling y el http streaming... siendo el http streaming lo que tenia en mente...

muchas gracias...