Cluster en Tomcat
01.07.2013 16:45La primera vez que tuve que asegurar alta disponibilidad a una aplicación Web pensé en el balanceo de Cargas. Este es aún un concepto muy común en la ingeniería y, aunque sorprendente, no muchos tienen conocimiento de lo que significa ni la alta disponibilidad, ni el balanceo de cargas y mucho menos como funcionan los clústeres o para qué sirven y cómo estos se relacionan en todo este tema. Ahora, con el boom de cloud computing, muchos consideran muerta la preocupación acerca de disponibilidad, rendimiento y demás atributos de calidad que el cloud ofrece tajántamente, claro, a través de representantes de ventas de distintas empresas. Explícitamente me refiero a que muchos ejecutivos consideran que desplegar un producto ya desarrollado en windows azure (por ejemplo), ya pasa a ser SaaS. Para aquellos recomiendo leer un poco acerca de Escalabilidad y Elasticidad.
Luego contaré de mis experiencias con Cloud Computing, por ahora hablaremos de Clústeres. En todo caso, cualquiera sea el objetivo que se le de a la implementación de un cluster para una aplicación Web, es en definitiva una característica que cualquier producto debería soportar para aprovechar las ventajas que la computación en la nube ofrece.
¿Qué es un CLuster?
Una vez comencé este artículo, me di cuenta que el concepto como tal ha sido usado desde algún momento desconocido y se ha mezclado en la ingeniería y en muchas otras disciplinas, no logré encontrar un origen ni una fuente en la literatura de la ingeniería que la defina detenidamente, tal vez por esto muchos desconocen sus características. De acuerdo a la experiencia, y algunos de los mayores proveedores de tecnología en el mundo, se coincide en lo siguiente:
Un Cluster es una configuración entre servidores o computadores. Estos deben estar contectados entre sí de tal forma que aparenten ser 1 sólo compartiendo la mayor cantidad de recursos posible[1][2].
Cuando se habla acerca de un cluster, se refiere principalmente a compartir recursos para el procesamiento desde el punto de vista de las máquinas, del hardware. Una aplicación puede aprovechar esta capacidad de procesamiento de 2 formas distintas:
- Para procesar complejos algoritmos computacionales o computación distribuida.
- Para garantizar disponibilidad a través del balanceo de cargas (El cuál es otro actor en este tema)
Ya que mi experiencia se centra en aplicaciones web usando Java como lenguaje de programación, en este caso, haremos uso de la segunda característica a través de un ejemplo en Tomcat. El siguiente diagrama (informal y previo a la implementación del ejemplo), muestra una configuración estándar de alta disponibilidad a través de cluster y balanceo:
En la imagen se diferencia la labor del balanceo de cargas, de la del cluster; El balanceo de cargas se encarga de repartir peticiones basado en diferentes algoritmos (En otro artículo lo detallaré con un nuevo ejemplo), mientras que el Cluster se encarga de garantizar que el balanceador siempre tendrá un servidor al cual redireccionar una petición sin que el usuario lo note.
¿Cómo lograrlo?
Cuando se desarrollan aplicaciones web en Java, existe bastantes conceptos alrededor del balanceo de cargas y cluster que pueden ser estudiados para comprender mucho mejor las opciones que el lenguaje habilita. Conceptos como pasivación y activación, serialización, replicación de sesiones, multidifusión, NTP, etc, los cuales están fuera del alcance de este artículo pero que es recomendable conocer.
Todo lo que acá se documenta está basado en la documentación de Apache Tomcat, principales guías que me han permitido lograr características de alta disponibilidad en aplicaciones web [3], ver secciones de clustering y load balancing. En otros artículos mostraré cómo lograr lo mismo con jboss y glassfish.
Para el ejercicio se van a usar las siguientes herramientas:
- Apache tomcat 6.0.37 - JDK 1.6.0.43
- Una aplicación Web sencilla que haga uso de variables de session. El ejemplo utilizado puede ser descargado de aquí (Covertir la extensión a .zip y descomprimir) .
- Para simular una pequeña red de computadoras, mi configuración personal es un Mac Book Pro, con 1 máquina virtual, VirtualBox, la cual tiene Windows XP, lo que da una idea de lo potable que puede a llegar a ser la aplicación y el concepto clúster.
- Un editor de texto de preferencia, no es necesario un IDE, sólo vamos a modificar algunos archivos de configuración.
- Es necesario establecer una red entre las máquinas y hay una nota sencilla pero crucial en Tomcat que se debe tener en cuenta para que todo funcione correctamente, esta es:
Note: Remember that your session state is tracked by a cookie, so your URL must look the same from the out side otherwise, a new session will be created
Esto se traduce básicamente en que si un usuario accede a la aplicación a través de distintas máquinas y la URL varía drásticamente, aunque la replicación de la sesión se haga correctamente, cada petición creará sesiones distintas en cada uno de los miembros del cluster, lo que finalmente significa que la instancia que se accede no logrará obtener los atributos de la session replicada, pues tiene una nueva y la otra simplemente esta almacenada para otro Id. Para lograr que la URL de un aplicación desplegada en 2 máquinas distintas parezca ser la misma (En este caso, se refiere al nombre del host, protocolo y contexto nombre de contexto), se requiere usar algún tipo de redireccionamiento. En el caso de una red establecida a través de un router, es posible hacer redireccionamiento por puertos, por ejemplo. Otra forma, es adicionar al DNS local las direcciones IP de cada nodo del cluster tal que todas reciban el mismo nombre, así, en la URL, lo única que variaría sería el nombre.
Listas las herramientas, continuamos con el ejemplo. Tomcat ofrece varias configuraciones para lograr armar un cluster de servidores, la más básica no requiere la subscripción de los nodos y se logra simplemente agregando la línea
1. Construcción del cluster de Servidores
2. Configuración de cluster Tomcat
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8">
<Manager className="org.apache.catalina.ha.session.DeltaManager"
expireSessionsOnShutdown="false"
notifyListenersOnReplication="true"/>
<Channel className="org.apache.catalina.tribes.group.GroupChannel">
<Membership className="org.apache.catalina.tribes.membership.McastService"
address="239.255.255.249"
port="45564"
frequency="500"
dropTime="3000"/>
<Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
<Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
</Sender>
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
address="auto"
port="4000"
autoBind="100"
selectorTimeout="5000"
maxThreads="6"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
<Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatch15Interceptor"/>
</Channel>
<ClusterListener className="org.apache.catalina.ha.session.JvmRouteSessionIDBinderListener"/>
<ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
</Cluster>
3. Desplegar la aplicación
Todos los atributos que sean almacenados en sesión deben ser objetos Serializables. Esta es la principal restricción cuando se va a pasar una aplicación ya desarrollada a entorno cluster. En cada uno de los contenedores, la aplicación debe estar desplegada con el mismo nombre para el context-root. Se debe incluir el tag al final del descriptor de despliegue web.xml, esto debe lucir similar a la siguiente imagen:
4. Arrancar y probar
Con lo logrado actualmente se puede conseguir probar que la replicación de sesión funciona adecuadamente en un cluster pequeño. Si la configuración es correcta y el despliegue se hizo con éxito, se logrará acceder a cada instancia a través del context-root TestCluster. Es decir, para probar de manera independiente se deben tener 2 URL que pueden ser similares a las siguientes: https://testcluster:8080/TestCluster y https://testcluster:8081/Testcluster/.
Recordando que en mi caso el redireccionamiento lo hice por DNS local, el nombre testcluster apunta tanto a 192.168.43.241 como a 192.168.43.242 donde la direncia en la petición lo hará el número del puerto. En caso que esto no se logre, la petición en cada instancia generará una nueva sesión.
Como observarán en el código, la aplicación cuenta con 2 Listener que nos informa de lo que sucede cada vez que se crea o destruye una sesión (HttpSessionListener) y cada vez que se crea, modifica y elimina un atributo de session (HttpSessionAttributeListener) el cual, en un ambiente en desarrollo permite detectar atributos que no serán serializados en un cluster y que generará error en producción.
Probaremos la replicación de sesón de la siguiente forma:
- ingresar al primer nodo del cluster https://testcluster:8080/TestCluster
- digitar una palabra o nombre y dar clic en Enviar.
- El paso anterior reenviara a una página de resumen.
- Verificar que la otra instancia recibió la replicación ingresando a https://testcluster:8081/TestCluster/test.jsp
- El paso anterior debe mostrar el mismo resumen que en el paso 3, de esta forma ambas instancias contarán con la sesión replicada.
Finalmente, el siguiente diagrama muestra el diagrama de despliegue que resume todos los pasos anteriores:
Enlaces electrónicos
[1] https://docs.oracle.com/cd/B28359_01/rac.111/b28254/rac_glossary.htm#CHDIADDF, visitado el 10 de junio de 2013
[2] https://es.wikipedia.org/wiki/Clúster_(informática), visitado el 10 de junio de 2013
[3] https://tomcat.apache.org/tomcat-6.0-doc/, visitado el 27 de junio de 2013
—————