Estás en:

Consultor tecnológico de desarrollo de proyectos informáticos.

 Ingeniero Técnico en Telecomunicaciones

Puedes encontrarme en Autentia: Ofrecemos servicios de soporte a desarrollo, factoría y formación

Somos expertos en Java/J2EE

Fecha de publicación del tutorial: 2007-01-03
Tutorial visitado 16.128 veces16.128
Descargar el tutorial en PDF


Regístrate para votar
Share |
JBOSS EN CLUSTER. EJEMPLO DE ALTA DISPONIBILIDAD JBOSS EN CLUSTER. EJEMPLO DE ALTA DISPONIBILIDAD

Los ejemplos de este tutorial están hechos con el siguiente entorno de desarrollo:
  • JBoss Eclipse IDE Milestone 5.
  • JDK 1.4
  • JBoss 4.0.5 GA


INTRODUCCION
.

Se pretende en este tutorial, y en posteriores tutoriales que tengo en mente realizar, enseñar a todo aquel que esté interesado en este poderoso servidor de aplicaciones las distintas posibilidades que ofrece JBoss para manejar temas de clustering, alta disponibilidad etc... en aplicaciones J2EE.

Además lo haré en base a ejemplos, que creo que es la forma más didáctica. Os propongo por lo tanto, el siguiente ejemplo.

Imaginaros que estáis trabajando para un concesionario de coches. El departamento de contabilidad, diariamente, ha de comunicar a la central el número de coches vendidos al día, al cierre del concesionario. El departamento de contabilidad os ha pedido estudiar la posibilidad de informatizar este proceso, y os comunica la importancia de que este proceso no falle. Sabemos además, que el departamento de informática de la empresa dispone de un webservice a través del cual se puede notificar esta información (no haremos esta parte, ya que no es el objetivo). De esta pequeña información podemos extraer las siguientes conclusiones:

  1. Necesitamos un proceso que se ejecute recursivamente a modo de cron. (Podríamos usar una tarea planificada de JBoss, es decir usar el servicio Scheduler).
  2. Sabemos, que nuestro proceso debe recoger la información de la base de datos (coches vendidos en el día) y enviar dicha información diariamente al cierre del concesionario. Si suponemos que nuestro concesionario cierra a las 20:00 horas y abre a las 10:00 horas, podemos suponer, que nuestra tarea recursiva debe conseguir enviar la información cada día durante el periodo de tiempo que va desde las 20:00 horas del día actual hasta las 10:00 horas del día siguiente. Una vez que lo consigue, rellena la información enviada en alguna tabla de base de datos indicando que ese día ya ha sido notificado. Toda esta parte la podemos resolver mediante un algoritmo sencillo.
  3. También nos han indicado la importancia que tiene que este proceso no falle. Evidentemente el departamento de contabilidad no sabe que lo que está pidiendo es que el proceso se realice en "alta disponibilidad". Es decir, necesitamos que el proceso se ejecute en dos máquinas distintas.
  4. Del apartado 3. Intuimos un problema. ¿ Podemos notificar dos veces a la central la misma información ? La respuesta ya os la digo yo...NO. Es necesario, que de alguna manera los procesos se sincronicen. Evidentemente, podríamos usar la base de datos como punto de sincronización y mediante algún algoritmo más o menos complejo lo conseguiríamos, pero... ¿ no existe alguna manera más sencilla ?. Ahí es donde entra la alta disponibilidad de JBoss . Realmente lo que nosotros necesitamos es que nuestra tarea planificada se despliegue en ambos servidores de aplicaciones, pero sólo uno de ellos debe estar activo, es decir en modo activo-pasivo, o mejor dicho "maestro-esclavo", lo que JBoss denomina HA-Singleton.

Ya tenemos la idea más o menos clara. Vamos a empezar a desarrollar nuestro ejemplo.

LA TAREA PLANIFICADA
.

No me voy a detener a explicar el servicio Scheduler de JBoss.
Os dejo el enlace al tutorial: http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=jbossScheduler

Empecemos abriendo nuestro eclipse. Nos creamos un proyecto nuevo para nuestro ejemplo. (No olvidéis añadir las librerías de JBoss para poder compilar la tarea planificable)

Empezaremos creando un interfaz que llamaremos INotificadorCentral:

package com.autentia.tutoriales.jboss;

public interface INotificadorCentral {
    /**
     * Este método debe acceder a Base de datos para comprobar
     * si el día ya ha sido notificado a la central
     * */   
    public boolean diaNotificado();
   
    /**
     * Este método debe comprobar si estoy o no en periodo de
     * notificación, es decir desde el cierre del concesionario del día
     * de notificación hasta la apertura del concesionario al día siguiente 
     */
    public boolean isPeriodoNotificacion();
   
    /**
     * Este metodo accede a base de datos, recopila la información y
     * envía mediante un webservice la información a la central y
     * almacena en base de datos la información notificada si todo ha
     * ido bien.
     */   
    public void notificayGuarda();        
}


Nos crearemos ahora la clase NotificadorCentral:

package com.autentia.tutoriales.jboss;

import java.util.Date;
import org.jboss.varia.scheduler.Schedulable;

public class NotificadorCentral implements INotificadorCentral, Schedulable {

    public boolean diaNotificado() {
        System.out.println("COMPRUEBO SI EL DÍA YA HA SIDO NOTIFICADO");
        return false;
    }

    public boolean isPeriodoNotificacion() {
        System.out.println("COMPRUEBO SI ESTOY EN PERIODO DE NOTIFICACION");
        return true;
    }

    public void notificayGuarda() {
        System.out.println("RECOJO INFORMACIÓN DE BASE DE DATOS.");
        System.out.println("NOTIFICO A LA CENTRAL.");
        System.out.println("GUARDO EN BASE DE DATOS.");
    }

    public void perform(Date arg0, long arg1) {
        if(diaNotificado())
            return;
       
        if(!isPeriodoNotificacion())
            return;
       
        notificayGuarda();
       
    }

}

Crearemos a continuación el descriptor del servicio: (jboss-service.xml)

<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.varia.scheduler.Scheduler" name="notificador:service=Scheduler">
<attribute name="StartAtStartup">true</attribute>
<attribute name="SchedulableClass"> com.autentia.tutoriales.jboss.NotificadorCentral </attribute>
<attribute name="InitialStartDate">NOW</attribute>
<attribute name="SchedulePeriod">15000</attribute>
<attribute name="InitialRepetitions">-1</attribute>
</mbean>
</server>

Nos creamos el fichero "notificador.sar":
    - com/...
    - META-INF/jboss-service.xml

Ahora ya tenemos nuestro servicio preparado y empaquetado.
Vamos a probar a ver si funciona correctamente en uno de los servidores. Lo voy a desplegar en default/deploy: 
 Y arrancamos el servidor en modo default (run.bat -c default) y mostramos la consola:

Consola

Una vez probado, empezaremos a probar las caracteristicas de alta disponibilidad.
Para ello, deberemos trabajar en modo "all". Arrancaremos nuestro servidor 
de la siguiente manera: (run.bat -c all)


CONFIGURANDO EL CLUSTER .

Lo primero que debemos hacer es configurar el cluster de nuestros servidores de aplicaciones.

En mi caso, yo tengo dos máquinas windows trabajando en una red local, cada una de ellas con el servidor de aplicaciones JBoss 4.0.5 instalado.
Las IPs de las máquinas son: 192.168.1.2  y 
192.168.1.13

JBoss, para la comunicación entre las máquinas del cluster usa JGroups y se puede configurar de dos modos esta comunicación, por UDP o por TCP. Nosotros para nuestro ejemplo usaremos TCP.


 Editaremos en ambas máquinas el siguiente fichero:
<ruta_instalacion_jboss> \server\all\deploy\cluster-service.xml
Buscaremos la etiqueta: <attribute name="PartitionConfig">

Y comentaremos la parte de configuración por UDP y descomentaremos la parte de configuración por TCP. La configuración por TCP quedará en la máquina 192.168.1.2 de la siguiente manera:


  <Config>
            <TCP bind_addr=" 192.168.1.2 " start_port="7800" loopback="true"
                 recv_buf_size="2000000" send_buf_size="640000"
                 tcp_nodelay="true" up_thread="false" down_thread="false"/>
            <TCPPING initial_hosts=" 192.168.1.2 [7800], 192.168.1.13 [7800]" port_range="3" timeout="3500"
               num_initial_members="3" up_thread="false" down_thread="false"/>
            <MERGE2 min_interval="5000" max_interval="10000"
               up_thread="false" down_thread="false"/>
            <FD_SOCK down_thread="false" up_thread="false"/>
            <FD shun="true" up_thread="false" down_thread="false"
               timeout="10000" max_tries="5"/>
            <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
            <pbcast.NAKACK up_thread="false" down_thread="false" gc_lag="100"
               retransmit_timeout="300,600,1200,2400,4800"/>
            <pbcast.STABLE desired_avg_gossip="20000" max_bytes="400000"
               down_thread="false" up_thread="false" />
            <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
               print_local_addr="true"  up_thread="false" down_thread="false"/>
            <FC max_credits="2000000" down_thread="false" up_thread="false"
                 min_threshold="0.10"/>
            <FRAG2 frag_size="60000" down_thread="false" up_thread="true"/>
            <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
  </Config>


En la máquina 192.168.1.13 quedará:

<Config>
            <TCP bind_addr=" 192.168.1.13 " start_port="7800" loopback="true"
                 recv_buf_size="2000000" send_buf_size="640000"
                 tcp_nodelay="true" up_thread="false" down_thread="false"/>
            <TCPPING initial_hosts=" 192.168.1.2 [7800], 192.168.1.13 [7800]" port_range="3" timeout="3500"
               num_initial_members="3" up_thread="false" down_thread="false"/>
            <MERGE2 min_interval="5000" max_interval="10000"
               up_thread="false" down_thread="false"/>
            <FD_SOCK down_thread="false" up_thread="false"/>
            <FD shun="true" up_thread="false" down_thread="false"
               timeout="10000" max_tries="5"/>
            <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
            <pbcast.NAKACK up_thread="false" down_thread="false" gc_lag="100"
               retransmit_timeout="300,600,1200,2400,4800"/>
            <pbcast.STABLE desired_avg_gossip="20000" max_bytes="400000"
               down_thread="false" up_thread="false" />
            <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
               print_local_addr="true"  up_thread="false" down_thread="false"/>
            <FC max_credits="2000000" down_thread="false" up_thread="false"
                 min_threshold="0.10"/>
            <FRAG2 frag_size="60000" down_thread="false" up_thread="true"/>
            <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
  </Config>



Con esto ya tendríamos configurado el cluster. Recomiendo también configurar el cluster de Tomcat para evitarnos algunos errores en el arranque. Para ello,
editaremos en ambas máquinas el siguiente fichero:

<ruta_instalacion_jboss> \server\all\deploy\tc5-cluster.sar\META-INF\jboss-service.xml

Buscaremos la etiqueta:  <attribute name="ClusterConfig">

Y comentaremos la parte de configuración por UDP y descomentaremos la parte de configuración por TCP. La configuración por TCP quedará en la máquina 192.168.1.2 de la siguiente manera:

<config>
              <TCP bind_addr=" 192.168.1.2 " start_port="7810" loopback="true"
                   tcp_nodelay="false" down_thread="false" up_thread="false"/>
              <TCPPING initial_hosts=" 192.168.1.2 [7810], 192.168.1.13 [7810]" port_range="3"   timeout="3500"
                 num_initial_members="3" up_thread="false" down_thread="false"/>
              <MERGE2 min_interval="5000" max_interval="10000"
                 up_thread="false" down_thread="false"/>
              <FD_SOCK down_thread="false" up_thread="false"/>
              <FD shun="true" up_thread="false" down_thread="false"
                 timeout="10000" max_tries="5"/>
              <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
              <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
                 retransmit_timeout="3000"/>
              <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
              <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
                 print_local_addr="true" down_thread="false" up_thread="false"/>
              <FC max_credits="2000000" down_thread="false" up_thread="false"
                 min_threshold="0.10"/>
              <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
              <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
</config>


En la máquina 192.168.1.13 quedará:
 
<config>
              <TCP bind_addr=" 192.168.1.13 " start_port="7810" loopback="true"
                   tcp_nodelay="false" down_thread="false" up_thread="false"/>
              <TCPPING initial_hosts=" 192.168.1.2 [7810], 192.168.1.13 [7810]" port_range="3"   timeout="3500"
                 num_initial_members="3" up_thread="false" down_thread="false"/>
              <MERGE2 min_interval="5000" max_interval="10000"
                 up_thread="false" down_thread="false"/>
              <FD_SOCK down_thread="false" up_thread="false"/>
              <FD shun="true" up_thread="false" down_thread="false"
                 timeout="10000" max_tries="5"/>
              <VERIFY_SUSPECT timeout="1500" down_thread="false" up_thread="false" />
              <pbcast.NAKACK down_thread="false" up_thread="false" gc_lag="100"
                 retransmit_timeout="3000"/>
              <pbcast.STABLE desired_avg_gossip="20000" down_thread="false" up_thread="false" />
              <pbcast.GMS join_timeout="5000" join_retry_timeout="2000" shun="true"
                 print_local_addr="true" down_thread="false" up_thread="false"/>
              <FC max_credits="2000000" down_thread="false" up_thread="false"
                 min_threshold="0.10"/>
              <FRAG2 frag_size="60000" down_thread="false" up_thread="false"/>
              <pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
</config>


Ahora, lanzaremos ambos servidores para observar el arranque:
(si tenéis algún firewall, podéis tener problemas)

Arranque del servidor

Podéis observar como ambos servidores están arrancados en cluster.

DESPLEGANDO EL SERVICIO EN DEPLOY:

Lo primero que haremos será desplegar nuestro servicio de manera normal, es decir, en el directorio <ruta_instalacion_jboss> \server\all\deploy de ambas máquinas. En este caso comprobaremos que funcionan ambas de manera independiente:

Mostramos la consola del primer servidor (192.168.1.2)

Primera máquina

En la segunda máquina:

Máquina dos

Comprobamos como los servicios trabajan de forma independiente.


DESPLEGANDO EL SERVICIO EN CLUSTER.

Bueno, ahora, aprovechemos la posibilidad que ofrece JBoss de desplegar un módulo en todos los nodos del cluster de una sola vez. Eliminaremos el fichero notificaciones.sar de los directorios "deploy" de ambas máquinas y lo copiaremos en el directorio:
<ruta_instalacion_jboss> \server\all\farm de una de las máquinas:
y observemos lo que ocurre:

En la máquina donde hemos copiado el fichero:
1. Se intenta enviar a todos los nodos del cluster el fichero notificador.sar
    "Start push of file notificador.sar to cluster"
2. Se inicia el servicio.

Copiado el fichero en cluster

En la otra máquina del cluster:
1. Se recibe el fichero y se despliega en local:

Recepción del fichero

Hemos conseguido que únicamente tengamos que desplegar el fichero en una de las máquinas del cluster, pero aún no hemos consegudo nuestro objetivo de trabajar en modo HA-Singleton.


DESPLEGANDO EL SERVICIO EN  MODO HA-SINGLETON.

Volvamos al descriptor de despliegue de nuestro Notificador (jboss-service.xml) y añadamos la siguiente dependencia:

<?xml version="1.0" encoding="UTF-8"?>
<server>
<mbean code="org.jboss.varia.scheduler.Scheduler" name="notificador:service=Scheduler">
<depends>jboss.ha:service=HASingletonDeployer,type=Barrier</depends>
<attribute name="StartAtStartup">true</attribute>
<attribute name="SchedulableClass">com.autentia.tutoriales.jboss.NotificadorCentral</attribute> <attribute name="InitialStartDate">NOW</attribute>
<attribute name="SchedulePeriod">15000</attribute>
<attribute name="InitialRepetitions">-1</attribute>
</mbean>
</server>

Regeneremos el fichero notificador.sar y volvamos a desplegar el fichero en:
<ruta_instalacion_jboss> \server\all\farm de alguna de las máquinas y observemos que ocurre:

En la máquina donde hemos copiado el fichero, no ha ocurrido ningún cambio con respecto a lo anterior:
    1. Copia al cluster el fichero y
    2. Arranca de nuevo el servicio

HA

¿ Y en la otra máquina ?

Observemos la consola:

HA pasivo

El servicio se ha desplegado pero...no ha arrancado. ¿Por qué?.
Al poner la dependencia anterior, hemos configurado el servicio para que sólo arranque en uno de los nodos (el maestro).  Vamos a parar el nodo maestro (shutdown.bat -S), a ver que ocurre:

En la máquina maestra:

Paramos el maestro

En la segunda máquina:

La segunda maquina

Vemos como se arranca el servicio, es decir, esta máquina pasa a ser la máquina maestra del cluster.

Volvamos a arrancar la máquina que hemos parado:

Nuevo arranque

Vemos como el servicio no se arranca. ¿ Y en la otra máquina ?

Mestra

Vemos como se detecta el arranque del otro servidor, pero continúa la ejecución del servicio.

Bueno, pues podéis seguir haciendo pruebas de parar una máquina, arrancar la otra, etc..., pero creo que hemos conseguido lo que buscábamos.

Espero que os haya servido el tutorial, si queréis que os ayudemos ya sabéis:
http://www.autentia.com

Anímate y coméntanos lo que pienses sobre este TUTORIAL:

Puedes opinar o comentar cualquier sugerencia que quieras comunicarnos sobre este tutorial; con tu ayuda, podemos ofrecerte un mejor servicio.


(Sólo para usuarios registrados)

» Registrate y accede a esta y otras ventajas «

Comentarios

  1. Francisco Javier Mar
    2007-01-17 - 05:57:36 PM

    En efecto, esta hecho para trabajar con la configuración "all" : run -c all Lo he dado por supuesto sin darme cuenta. gracias.

  2. Oscar Ivan
    2007-01-09 - 04:39:39 AM

    Me parece un excelente tutorial. Encontre un detalle que puede ser 'insignificante': El tutorial esta hecho suponiendo que se usara el dominio o configuracion "all" de jboss, aun que la configuracion/dominio por defecto es "default"; lo que hay que hacer es mover los siguientes jar's de all/lib a default/lib (o el dominio a usar): - jbossha.jar - jgroups.jar - jboss-cache.jar Ademas de copiar el directorio tc5-cluster.sar al dominio a usar, asi como copiar el directorio farm. Finalmente es necesario copiar el archivo cluster-service.xml, igualmente, al directorio del dominio a usar. Saludos, y en espera de los siguientes tutoriales.