Compile aplicaciones de Java servidor con clústeres

Apache ZooKeeper y LinkedIn Project Norbert facilitan la coordinación servidor-grupo en aplicaciones de Java empresariales distribuidas

La agrupación en clúster se ha convertido en el lugar común para el desarrollo de aplicaciones empresariales de Java altamente escalables, pero la consciencia de servidor-clúster a nivel de la aplicación actualmente no forma parte de Java EE. En este artículo, Mukul Gupta y Paresh Paladiya le muestran cómo aprovechar dos proyectos de código abierto, Apache ZooKeeper y Project Norbert de LinkedIn, para coordinación servidor-grupo en aplicaciones de Java empresariales distribuidas.

Mukul Gupta, Senior Technical Architect, CGI

Mukul ha estado diseñando y desarrollando principalmente grandes aplicaciones de escala empresarial durante los últimos 15 años utilizando una variedad de lenguajes y tecnologías, incluyendo C++, Forte, Scala, JavaScript y el lenguaje de Java.



Paresh Paladiya, Senior Technical Architect, CGI

Paresh es un tecnólogo sénior que ha trabajado durante 12 años en ciclos de vida de desarrollo de sistema completo. Se especializa en metodologías de diseño orientado al objeto y desarrollo ágil, integración de la SOA y el sistema y arquitecturas de internet y de cliente/servidor.



11-02-2013

Muchas aplicaciones empresariales actualmente son entregadas por un conjunto de procesos y servidores cooperativos y distribuidos. La posibilidad de servidor-agrupación en clúster, por ejemplo, está disponible para solicitudes web para casi todos los servidores empresariales de Java populares, lo que también puede proporcionar opciones de configuración limitadas como pesos del servidor y recargas de configuración.

Aunque la mayoría de los servidores empresariales de Java tienen soporte incorporado para agrupación en clúster, ese soporte no está tan disponible en el nivel de la aplicación para casos de uso personalizados. Como desarrolladores de software, ¿cómo deberíamos gestionar casos de uso que involucren la coordinación de tareas distribuidas o que impliquen el soporte de aplicaciones de inquilinos múltiples? (Una aplicación de inquilinos múltiples es aquella que requiere que se aíslen instancias o subconjuntos del clúster de servidor general o grupo.) Para estos tipos de casos de uso, debemos encontrar una forma de hacer que las posibilidades de coordinación de grupos estén disponibles en la capa de software de la aplicación, preferiblemente a un alto nivel de abstracción.

En este artículo, presentamos una guía para incorporar posibilidades de membresía y gestión de grupo en aplicaciones de Java distribuidas. Comenzaremos con una aplicación de Java basada en Spring Integration, la cual compilaremos con una capa de abstracción servidor-clúster con base en dos proyectos de código abierto: Apache ZooKeeper y Project Norbert de LinkedIn.

Sobre ZooKeeper y Project Norbert

Apache ZooKeeper es un proyecto de código abierto (vea Resources) que proporciona posibilidades de coordinación servidor-grupo para aplicaciones distribuidas. Desarrollado por LinkedIn, Project Norbert expone las posibilidades de gestión de grupo-servidor y solicitud-enrutamiento de ZooKeeper a un nivel más alto de abstracción, haciéndolo más accesible para desarrolladores de aplicaciones de Java.

Visión general de servidor-agrupación en clúster

Las aplicaciones conscientes de servidor-clúster normalmente requieren al menos una de las siguientes posibilidades:

  • Membresía de grupo con mantenimiento de estado y posibilidad de consultas: La membresía de grupo en tiempo real es necesaria para distribuir el procesamiento en un conjunto activo de servidores. Para gestionar la membresía de grupo, una aplicación debe poder establecer un grupo de proceso/servidor y realizar el seguimiento del estado de todos los servidores en ese grupo. También debe poder notificar a los servidores activos cuando un servidor está inactivo o vuelve a activarse. La aplicación enrutará y equilibrará la carga de solicitudes de servicio solo entre los servidores activos en el clúster, ayudando así a garantizar servicios altamente disponibles.
  • Un proceso primario o líder: Un proceso en el clúster asume la responsabilidad por la función grupo-coordinación de mantener la sincronización del estado a través del clúster del servidor. El mecanismo de seleccionar el proceso líder es un caso especial de un conjunto más amplio de problemas conocidos como consenso distribuido. (Los compromisos de dos y tres fases son problemas bien conocidos de consenso distribuido.)
  • Coordinación de tareas y elección dinámica del servidor líder: A nivel de la aplicación, el servidor líder es responsable de la coordinación de tareas, lo cual hace al distribuir las tareas entre otros servidores (seguidor) en el clúster. Tener un servidor líder elimina el potencial de contención entre servidores, lo que de otra forma requeriría algún tipo de exclusión mutua o bloqueo para habilitar tareas elegibles para su ejecución. (Este sería el caso, por ejemplo, si los servidores estuvieran sondeando tareas a partir de un almacén de datos común.) La elección dinámica del líder es lo que hace que el procesamiento distribuido sea confiable; si un servidor líder se cuelga, un nuevo líder puede ser elegido para continuar las tareas de aplicación de procesamiento.
  • Comunicación de grupo: Una aplicación en una aplicación consciente del clúster debe poder facilitar el intercambio eficiente de datos estructurados y de comandos a través del clúster del servidor.
  • Bloqueos distribuidos y datos compartidos: Las aplicaciones distribuidas deben poder acceder a dispositivos como bloqueos distribuidos y estructuras de datos compartidos como colas y correlaciones, si se requiere.

Ejemplo de caso de uso: Spring Integration

Nuestro caso de uso representativo es un escenario de integración de aplicación empresarial (EAI) que solucionaremos con una aplicación simulada con base en Spring Integration. La aplicación tiene las siguientes características y requisitos:

  1. Una aplicación de origen simulada produce eventos relacionados con la integración y mensajes como parte de su procesamiento transaccional regular y los almacena en un almacén de datos.
  2. Los eventos de integración y los mensajes son procesados por un conjunto distribuido de procesos de Java (un clúster de servidor) que pueden ejecutarse en la misma máquina de servidor o estar distribuidos a través de máquinas conectadas por una red en funcionamiento. La agrupación en clúster del servidor es necesaria para la escalabilidad y para la alta disponibilidad.
  3. Cada evento de integración es procesado solo una vez por cualquier miembro de clúster (es decir, una JVM dada). Los mensajes de salida son comunicados a aplicaciones de asociados en la intranet o en internet, según sea aplicable.

Figura 1 muestra el saliente del evento de integración y el flujo de procesamiento de mensajes de la aplicación de origen simulada.

Figura 1. Esquema de la aplicación de ejemplo basada en Spring Integration
Esquema de la aplicación de ejemplo basada en Spring Integration

Diseñando la arquitectura de una solución

Nuestra primera etapa en el desarrollo de una solución para este caso de uso es distribuir la aplicación de integración para que se ejecute en un clúster de servidor. Esto debe incrementar el rendimiento del procesamiento y garantizar la alta disponibilidad y escalabilidad. La falla de un proceso individual no detendrá el procesamiento de la aplicación en su totalidad.

Una vez distribuida, la aplicación de integración extraerá eventos de integración del almacén de datos de la aplicación. Un servidor individual del clúster de servidores extraerá eventos de aplicación del almacén de eventos mediante un adaptador de aplicación adecuado y después distribuirá esos eventos hacia el resto de los servidores en el clúster para su procesamiento. Este servidor individual entonces asume el rol de servidor líder o coordinador de tareas, responsable de distribuir eventos (tareas de procesamiento) a través del resto del clúster.

Los miembros de clúster del servidor que soportan la aplicación de integración se vuelven conocidos al momento de la configuración. La información de membresía de clúster es dinámicamente distribuida a todos los servidores operativos a medida que los nuevos miembros del servidor son iniciados o cuando cualquier servidor se cuelga o deja de estar activo. Asimismo, el servidor de coordinador de tareas es elegido dinámicamente; si el servidor de coordinador de tareas se cuelga o deja de estar disponible, un servidor líder alternativo será elegido en forma cooperativa de entre los servidores en operación restantes. Los eventos de integración pueden ser procesados por una de las muchas infraestructuras de Java de código abierto que soportan los Patrones de Integración Empresarial (EIP) (vea Resources).

Figura 2 muestra el esquema y los componentes de la solución del caso de uso, lo cual describiremos en la siguiente sección.

Figura 2. Esquema y componentes del clúster de servidor de la solución del caso de uso
Esquema y componentes del clúster de servidor de la solución del caso de uso

El clúster de servidor

Nuestra aplicación de integración requiere de dispositivos relacionados con el grupo de servidores que no están disponibles de fábrica en Java Standard Edition (Java SE) o en Java Enterprise Edition (Java EE). Ejemplos de estos incluyen la agrupación en clúster de servidores y la elección dinámica del servidor líder.

Figura 3 muestra las herramientas de código abierto que utilizaremos para implementar nuestra solución de EAI; es decir, Spring Integration para procesamiento de eventos y Apache ZooKeeper y LinkedIn Project Norbert para habilitar la consciencia de clúster.

Figura 3. Correlacionamiento de tecnología del clúster de servidor
Correlacionamiento de tecnología del clúster de servidor

Sobre la aplicación simulada

El propósito de la aplicación simulada es demostrar el uso de Apache ZooKeeper y Project Norbert para solucionar los retos comunes del desarrollo de clústeres de servidor basados en Java. La aplicación funciona de la siguiente manera:

  • El almacén de eventos de aplicación es simulado por una carpeta compartida que puede ser accedida por todos los servidores dentro del clúster de servidor de integración.
  • Los archivos (y sus datos) que van al repositorio de esta carpeta compartida son utilizados para simular eventos de integración. Un script externo también puede ser utilizado para crear archivos continuamente, simulando así la creación de eventos.
  • Un componente de sondeo de archivos basado en Spring Integration (el adaptador de evento entrante) extrae eventos del almacén de eventos de aplicación, el cual es simulado por la carpeta del sistema de archivos compartida. Los datos del archivo son entonces distribuidos hacia el resto de los miembros de clúster de servidor para su procesamiento.
  • El procesamiento de eventos es simulado al poner un prefijo a los datos de archivo con información breve de tipo cabecera como id de servidor y indicación de fecha y hora.
  • Las aplicaciones de asociado son simuladas por algunas otras carpetas de sistema de archivos compartidas, una para cada aplicación de asociado.

Ahora ya tiene una visión general del caso de uso de ejemplo, nuestra arquitectura de solución propuesta y la aplicación simulada. Ya estamos listos para presentar los dos componentes principales de nuestro clúster de servidor y solución de distribución de tareas: Apache ZooKeeper y Project Norbert de LinkedIn.

Apache ZooKeeper y Project Norbert

Desarrollado primero por Yahoo Research, ZooKeeper fue inicialmente adoptado por Apache Software Foundation como un subproyecto de Hadoop. Actualmente es un proyecto de nivel superior que proporciona servicios de coordinación de grupo distribuidos. Utilizaremos ZooKeeper para crear el clúster de servidor que aloja nuestra aplicación simulada. ZooKeeper también implementará la funcionalidad de elección de servidor líder que nuestra aplicación requiere. (La elección del líder es esencial para todas las otras funciones de coordinación de grupo que ZooKeeper ofrece.)

Los servidores de ZooKeeper habilitan la coordinación de servidores mediante un modelo de datos en memoria, replicado y jerárquico de tipo de sistema de archivos de znodes (nodos de datos de ZooKeeper). Igual que los archivos, los znodes pueden alojar datos, pero igual que los directorios también pueden alojar znodes hijos.

Hay dos tipos de znodes: los znodes regulares son explícitamente creados y suprimidos por procesos de cliente, por quanto que los znodes efímeros son automáticamente suprimidos cuando la sesión de cliente originadora se va. Cuando un znode regular o efímero es creado con un distintivo secuencial, un sufijo creciente en forma monotónica de 10 dígitos es adjuntado al nombre del znode.

Más sobre ZooKeeper:

  • ZooKeeper garantiza que cuando los servidores son iniciados, cada uno de ellos estaba consciente de los puertos receptores de los otros servidores en el grupo. Los puertos receptores soportan los servicios que facilitan la elección del líder, la comunicación de puerto a puerto y las conexiones de cliente a servidores.
  • ZooKeeper utiliza un algoritmo de consenso de grupo para elegir un líder, después de lo cual los otros servidores son conocidos como seguidores. Un número mínimo de servidores — un cuórum — es necesario para que el clúster de servidor opere.
  • Los procesos de cliente tienen un conjunto definido de operaciones disponibles, el cual utilizan para orquestar lecturas y actualizaciones del modelo de datos con base en znodes.
  • Todas las grabaciones son enrutadas mediante el servidor líder, el cual limita la escalabilidad de operaciones de grabación. El líder utiliza un protocolo de transmisión llamado ZooKeeper Atomic Broadcast (ZAB) para actualizar los servidores de seguidor. ZAB preserva el pedido de actualización. El modelo de datos de tipo sistema de archivos en memoria es entonces sincronizado eventualmente en todos los servidores en un grupo o clúster. El modelo de datos también es grabado en disco periódicamente mediante instantáneas persistentes.
  • Las lecturas son mucho más escalables que las grabaciones. Los seguidores responden a lecturas de proceso de cliente desde esta copia sincronizada del modelo de datos.
  • Los znodes soportan un mecanismo de devolución de llamada de una sola vez para clientes, llamado "observador". El observador desencadena una señal en el proceso de cliente de supervisión sobre los cambios en znodes observados.

Gestión de grupo con Project Norbert

Project Norbert de LinkedIn se engancha en el clúster basado en Apache ZooKeeper para proporcionar aplicaciones con consciencia de membresía de clúster de servidor. Norbert hace esto dinámicamente, al momento de la ejecución. Norbert también deriva un servidor de JBoss Netty y proporciona un módulo de cliente correspondiente para habilitar las aplicaciones para que intercambien mensajes. Tenga en cuenta que versiones anteriores de Norbert requirieron el uso de la biblioteca para mensajes de serialización de objetos Google Protocol Buffers. La versión actual soporta serialización de objetos personalizada. (Vea Resources para aprender más.)

Spring Integration

Spring Integration es una infraestructura de código abierto que atiende los retos de EAI. Soporta los Patrones de Integración Empresarial mediante componentes declarativos y se basa en el modelo de programación de Spring.

Compilando un clúster de servidor

Con todos los componentes en su lugar, estamos listos para comenzar la configuración de nuestro clúster de servidor de procesamiento de eventos. La primera etapa para el clúster es establecer el cuórum de servidores, después de lo cual el servidor líder recién electo automáticamente inicia su flujo de agrupamiento de archivos locales. El agrupamiento de archivos sucede mediante Spring Integration, lo cual simula un adaptador de eventos de aplicación entrante. Los archivos agrupados, simulando eventos de aplicación, son distribuidos en servidores disponibles utilizando una estrategia de distribución de tareas por turnos.

Tenga en cuenta que ZooKeeper define un cuórum válido como la mayoría de los procesos de servidor. Por lo tanto, un clúster mínimo consiste en tres servidores, con el cuórum siendo establecido cuando al menos dos de los servidores están activos. Adicionalmente, cada servidor en nuestra aplicación simulada requiere dos archivos de configuración: un archivo de propiedades para que sea utilizado por la lógica de controlador que hace el programa de arranque de la JVM de servidor general y un archivo de propiedades separado para el clúster de servidor basado en ZooKeeper (del cual cada servidor es una parte).

Etapa 1: Crear un archivo de propiedades

Server.java (vea Resources) es el controlador y clase de entrada que inicia nuestra aplicación de EAI distribuida. Los parámetros iniciales de la aplicación son leídos desde un archivo de propiedades, como se muestra en Listado 1:

Listado 1. Archivo de propiedades de servidor
# Each server in group gets a unique id:integer in range 1-255  
server.id=1

# zookeeper server configuration parameter file -relative path to this bootstrap file
zookeeperConfigFile=server1.cfg

#directory where input events for processing are polled for - common for all servers
inputEventsDir=C:/test/in_events

#directory where output / processed events are written out - may or may not be shared by 
#all servers
outputEventsDir=C:/test/out_events

#listener port for Netty server (used for intra server message exchange)
messageServerPort=2195

Note que un id de servidor (valor entero) exclusivo es necesario para cada uno de los servidores en este clúster de servidor mínimo.

El directorio de eventos de entrada es compartido por todos los servidores. El directorio de eventos de salida simula una aplicación de asociado y opcionalmente puede ser compartida por todos los servidores. La distribución de ZooKeeper proporciona una clase para analizar la información de configuración para cada uno de los servidores que componen el clúster de servidor o los "iguales de cuórum". Ya que nuestra aplicación reutiliza esta clase, requiere que la configuración de ZooKeeper esté en el mismo formato.

También tenga en cuenta que messageServerPort es el puerto receptor para el servidor de Netty (el cual es iniciado y gestionado por la biblioteca de Norbert).

Etapa 2: Crear un archivo de configuración para el servidor de ZooKeeper en proceso

Listado 2. Archivo de configuración para ZooKeeper (server1.cfg)
tickTime=2000
dataDir=C:/test/node1/data
dataLogDir=C:/test/node1/log
clientPort=2181
initLimit=5
syncLimit=2
peerType=participant
maxSessionTimeout=60000
server.1=127.0.0.1:2888:3888
server.2=127.0.0.1:2889:3889
server.3=127.0.0.1:2890:3890

Los parámetros mostrados en Listado 2 (así como algunos opcionales para los cuales los valores predeterminados son utilizados a menos que sean alterados temporalmente) son descritos en la documentación de ZooKeeper (vea Resources). Note que cada servidor de ZooKeeper utiliza tres puertos receptores. El clientPort (2181 en el archivo de configuración anterior) es utilizado por los procesos de cliente para conectarse al servidor; el segundo puerto receptor es utilizado para habilitar las comunicaciones de igual a igual (valor 2888 para el servidor 1); y el tercero habilita el protocolo de elección del líder (valor 3888 para el servidor 1). Cada servidor conoce la topología de servidor general del clúster, de forma que server1.cfg también hace una lista del servidor 2 y el servidor 3 y de sus puertos de igual a igual.

Etapa 3: Inicializar el clúster de ZooKeeper al inicio del servidor

La clase de controlador Server.java inicia una hebra separada (ZooKeeperServer.java) que deriva el miembro de clúster basado en ZooKeeper, como se muestra en Listado 3:

Listado 3. ZooKeeperServer.java
package ibm.developerworks.article;
…
public class ZooKeeperServer implements Runnable
{

   public ZooKeeperServer(File configFile) throws ConfigException, IOException
   {
      serverConfig = new QuorumPeerConfig();
      …
      serverConfig.parse(configFile.getCanonicalPath());
    }

      public void run()
   {
      NIOServerCnxn.Factory cnxnFactory;
      try
      {
         // supports client connections
         cnxnFactory = new NIOServerCnxn.Factory(serverConfig.getClientPortAddress(),
               serverConfig.getMaxClientCnxns());
         server = new QuorumPeer();

         // most properties defaulted from QuorumPeerConfig; can be overridden
         // by specifying in the zookeeper config file

         server.setClientPortAddress(serverConfig.getClientPortAddress());
         …
         server.start(); //start this cluster member

         // wait for server thread to die
         server.join();
      }
      …
   }

    …
   public boolean isLeader()
   {
      //used to control file poller.  Only the leader process does task
      // distribution
      if (server != null)
      {
         return (server.leader != null);
      }
      return false;
   }

Etapa 4: Inicializar el servidor de mensajería basado en Norbert

Después de que hemos establecido el cuórum de servidores, podemos iniciar el servidor de Netty basado en Norbert, el cual soporta una mensajería intra-servidor rápida.

Listado 4. MessagingServer.java
   public static void init(QuorumPeerConfig config) throws UnknownHostException
   {
       …
      // [a] client (wrapper) for zookeeper server - points to local / in process
      // zookeeper server
      String host = "localhost" + ":" + config.getClientPortAddress().getPort();

      //[a1] the zookeeper session timeout (5000 ms) affects how fast cluster topology 
      // changes are communicated back to the cluster state listener class

      zkClusterClient = new ZooKeeperClusterClient("eai_sample_service", host, 5000);

      zkClusterClient.awaitConnectionUninterruptibly();
      …
      // [b] nettyServerURL - is URL for local Netty server URL
      nettyServerURL = String.format("%s:%d", InetAddress.getLocalHost().getHostName(),
            Server.getNettyServerPort());
      …

      // [c]
      …
      zkClusterClient.addNode(nodeId, nettyServerURL);

      // [d] add cluster listener to monitor state
      zkClusterClient.addListener(new ClusterStateListener());

      //  Norbert - Netty server config
      NetworkServerConfig norbertServerConfig = new NetworkServerConfig();

      // [e] group coordination via zookeeper cluster client
      norbertServerConfig.setClusterClient(zkClusterClient);

      // [f] threads required for processing requests
      norbertServerConfig.setRequestThreadMaxPoolSize(20);

      networkServer = new NettyNetworkServer(norbertServerConfig);

      // [g] register message handler (identifies request and response types) and the
      // corresponding object serializer for the request and response
      networkServer.registerHandler(new AppMessageHandler(), new CommonSerializer());

      // bind the server to the unique server id
      // one to one association between zookeeper server and Norbert server
      networkServer.bind(Server.getServerId());   

   }

Tenga en cuenta que el servidor de mensajería basado en Norbert incluye un cliente para el clúster de ZooKeeper. Configure esto para conectarse al servidor de ZooKeeper local (en proceso), después cree un cliente para el servidor de ZooKeeper. El tiempo de espera excedido de la sesión afecta la velocidad con la que los cambios en la topología del clúster son comunicados a la aplicación. Esto crea efectivamente un pequeño marco de tiempo dentro del cual el estado registrado de la topología del clúster estará fuera de sincronía con el estado real de la topología del clúster, a medida que los nuevos servidores sean iniciados o que los existentes se cuelguen. Las aplicaciones necesitan hacer almacenamiento intermedio de mensajes y/o implementar la lógica de reintento para fallas de envío de mensajes durante este periodo de tiempo.

MessagingServer.java (Listado 4) hace lo siguiente:

  • Configura el punto final (URL) para el servidor de Netty.
  • Asocia el node Id o server Id local con el servidor de Netty configurado.
  • Asocia una instancia de un receptor de estado de clúster, lo cual describiremos un poco más adelante. Norbert utilizará esto para llevar los cambios en la topología del clúster de regreso a la aplicación.
  • Asigna el cliente de clúster de ZooKeeper a la instancia de configuración del servidor que está siendo llenada.
  • Asocia una clase exclusiva manejadora de mensajes para un par de solicitud/respuesta. Una clase serializadora también es necesaria para los objetos de solicitud y respuesta de ordenación y desordenación. (Puede acceder a las clases respectivas incluidas con el código de solución, disponibles en GitHub; vea Resources para obtener un enlace.)

También note que esa devolución de llamada de la aplicación en la mensajería requiere de una agrupación de hebras.

Etapa 5: Inicializar el cliente de clúster de Norbert

A continuación, inicializaremos el cliente de clúster de Norbert. MessagingClient.java, mostrado en Listado 5, configura el cliente de clúster y lo inicializa con una estrategia de equilibrio de carga:

Listado 5. MessagingClient.java
public class MessagingClient
{
   …
   public static void init()
   {
      …
      NetworkClientConfig config = new NetworkClientConfig();

      // [a] need instance of local norbert based zookeeper cluster client
      config.setClusterClient(MessagingServer.getZooKeeperClusterClient());

      // [b] cluster client with round robin selection of message target
      nettyClient = new NettyNetworkClient(config, 
                                 new RoundRobinLoadBalancerFactory());
      …
   }
   ...
    …
   // [c] – if server id <0 – used round robin strategy to choose target
   // else send to identified target server 
   public static Future<String> sendMessage(AppRequestMsg messg, int serverId)
         throws Exception
   {
      …
      // [d] load balance message to cluster- strategy registered with client
      if (serverId <= 0)
      {
         …
         return nettyClient.sendRequest(messg, serializer);
      }
      else
      {
         // [e] message to be sent to particular server node
         …
         if (destNode != null)
         {
            …
            return nettyClient.sendRequestToNode(messg, destNode, serializer);
         }
         …
      }
   }
   …
}

Note en Listado 5 que si el servidor de destino no es identificado por un valor de server Id positivo, el mensaje es enviado al seleccionar a un servidor de entre el grupo activo con base en la estrategia de equilibrio de carga configurada. Las aplicaciones pueden configurar sus propias estrategias e implementaciones de manejo de mensajes, quizá con base en atributos de servidor adicionales. (Considere aplicaciones de inquilino múltiple en las cuales las solicitudes pueden ser reenviadas a subclústeres de servidor identificados, uno para cada propietario; vea Resources para ver una discusión sobre el tema.)

Supervisión de estado y distribución de tareas

Existen tres componentes más para la aplicación simulada, los cuales describimos en las siguientes secciones:

  • Un componente para supervisar el estado (membresía de servidor) del clúster.
  • Un archivo de definición de flujo de Spring Integration. El archivo de definición de flujo define el flujo basado en EIP de mensajes desde la agrupación de tareas de la aplicación simulada hacia el distribuidor central de tareas. El distribuidor de tareas eventualmente enrutará cada tarea para su procesamiento hacia uno de los miembros de clúster disponibles.
  • Un distribuidor de tareas, el cual implementa el enrutamiento de tarea final en uno de los miembros del clúster.

El receptor del estado del clúster (topología)

El receptor del estado del clúster garantiza que el cliente de mensajería tiene la lista actualizada de nodos disponibles. También inicia la sola instancia de adaptador de eventos (el sondeo de archivos) en el servidor líder. El sondeo de archivos entrega los archivos sondeados al componente de procesador de mensajes (un POJO), el cual es el distribuidor de tareas real. Ya que hay solo una instancia de distribución de tareas dentro del clúster, no se requiere más sincronización a nivel de la aplicación. Listado 6 muestra el receptor del estado del clúster:

Listado 6. ClusterStateListener.java
public class ClusterStateListener implements ClusterListener
{
   …
   public void handleClusterNodesChanged(Set<Node> currNodeSet)
   {
      …
      // [a] start event adapter if this server is leader
      if (Server.isLeader() && !Server.isFilePollerRunning())
      {
         Server.startFilePoller();
      }

   }
   …
}

El sondeo de archivos basado en Spring Integration

El flujo de Spring Integration hace lo siguiente (como se demuestra en Listado 7):

  • Crea un canal o conducto de mensajes llamado messageInputChannel.
  • Define un adaptador de canal de entrada que sondea los archivos cada 10 segundos desde un directorio que es leído desde las propiedades del sistema de la JVM (es decir, property input.dir). Cualquier archivo que sea sondeado y ubicado es pasado al canal de mensajes, messageInputChannel.
  • Configura el distribuidor de tareas, un bean de Java, para recibir mensajes desde el canal de mensajes. Su método, processMessage, es invocado para ejecutar la función de distribución de tareas.
Listado 7. Flujo basado en Spring Integration — FilePoller_spring.xml
…
   <integration:channel id="messageInputChannel" />

    <int-file:inbound-channel-adapter channel="messageInputChannel"
        directory="file:#{ systemProperties['input.dir'] }"
        filename-pattern="*.*" prevent-duplicates="true" >

        <integration:poller id="poller" fixed-rate="10" />
    </int-file:inbound-channel-adapter>

    <integration:service-activator input-channel="messageInputChannel"
        method="processMessage" ref="taskDistributor" />

    <bean
        id="taskDistributor"
        class="ibm.developerworks.article.TaskDistributor" >
    </bean>
…

El distribuidor de tareas

El distribuidor de tareas contiene la lógica que enruta las solicitudes a través de los miembros de clúster. El componente de sondeo de archivos es activado solo en el servidor líder y pasa los archivos sondeados (en este caso eventos de integración simulados) al distribuidor de tareas. El distribuidor de tareas utiliza el módulo de cliente de Norbert para enrutar las solicitudes (las cuales son archivos sondeados derivados como mensajes) hacia los servidores activos en el clúster. El distribuidor de tareas es mostrado en Listado 8:

Listado 8. Distribuidor de tareas de flujo controlado de Spring Integration (un bean de Java)
{
   …
   // [a] invoked by spring integration context 
   public void processMessage(Message<File> polledMessg)
   {
      File polledFile = polledMessg.getPayload();
      …
      
      try
      {
         logr.info("Received file as input:" + polledFile.getCanonicalPath());

         // prefix file name and a delimiter for the rest of the payload
         payload = polledFile.getName() + "|~" + Files.toString(polledFile, charset);

         …
         // create new message
         AppRequestMsg newMessg = new AppRequestMsg(payload);

         // [b]load balance the request to operating servers without
         // targeting any one in particular
         Future<String> retAck = MessagingClient.sendMessage(newMessg, -1);

         // block for acknowledgement - could have processed acknowledgement
         // asynchronously by repositing to a separate queue
         String ack = retAck.get();
         …
         logr.info("sent message and received acknowledgement:" + ack);
      …
   }
}

Note que el método service activator es llamado por el contexto de control de Spring Integration después de que el sondeo de archivos ubica un archivo para su procesamiento. También tenga en cuenta que el contenido del archivo está serializado y forma la carga de un nuevo objeto de solicitud. El método sendMessage() del cliente de mensajería es invocado, pero sin apuntar a un servidor de destino en particular. El módulo del cliente de Norbert entonces hace un equilibrio de carga del mensaje resultante para uno de los servidores en operación en el clúster.

Ejecute la aplicación simulada

Un archivo JAR "ejecutable" (ZooKeeper-Norbert-simulated-app.jar) junto con archivos de configuración de muestra para un clúster de tres servidores están incluidos en el código de origen de este artículo (vea Resources).

Puede probar la aplicación al iniciar los tres servidores localmente en una sola máquina o distribuyéndolos en su red. Una carpeta común de entrada montada/accesible de red será necesaria para ejecutar la aplicación en más de una máquina. Puede incrementar el número de servidores en el clúster al crear los archivos de configuración correspondientes, dos por cada servidor adicional, y actualizar los archivos de configuración existentes.

Desencadene el procesamiento de eventos al copiar archivos, con el contenido de texto, en la carpeta de entrada designada. Los archivos sucesivos son manejados por distintos servidores. Pruebe la confiabilidad del servicio deteniendo uno de los servidores. (Sin embargo, tenga en cuenta que el cuórum para un clúster de tres servidores requiere que solo un servidor esté inactivo en cualquier momento para que la aplicación funcione.) De forma predeterminada, el archivo log4j.properties incluido tiene el registro cronológico habilitado al nivel TRACE; note que la topología del servidor será actualizada con los servidores que se están ejecutando. Si desactiva el servidor líder, entonces un nuevo líder será elegido y el flujo de sondeo de archivos será activado en ese servidor, garantizando así el procesamiento continuo.

Vea la sección Resources para aprender más sobre el uso de Apache ZooKeeper y Project Norbert para el desarrollo de aplicaciones conscientes del clúster de servidor.

Recursos

Aprender

Obtener los productos y tecnologías

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java
ArticleID=857692
ArticleTitle=Compile aplicaciones de Java servidor con clústeres
publish-date=02112013