Red Hat Training

A Red Hat training course is available for Red Hat JBoss Enterprise Application Platform

9.3. Implémenter un Singleton HA

Résumé

La procédure suivante illustre comment déployer un service qui se trouve dans un decorateur de SingletonService et qui est utilisé comme service singleton dans l'ensemble du cluster. Le service active une minuterie programmée, qui n'est lancée qu'à une seule reprise dans le cluster.

Procédure 9.3. Implémenter un Service HA Singleton

  1. Rédiger une application de Service HA Singleton

    Voici un exemple simple de Service se trouvant dans le decorator SingletonService devant être déployé comme service singleton. On trouvera un exemple complet dans le quickstart de cluster-ha-singleton qui est livré avec Red Hat JBoss Enterprise Application Platform 6. Ce quickstart contient toutes les instructions pour générer et déployer l'application.
    1. Créer un service.

      Voici un exemple de service :
      package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
      
      import java.util.Date;
      import java.util.concurrent.atomic.AtomicBoolean;
      
      import javax.naming.InitialContext;
      import javax.naming.NamingException;
      
      import org.jboss.logging.Logger;
      import org.jboss.msc.service.Service;
      import org.jboss.msc.service.ServiceName;
      import org.jboss.msc.service.StartContext;
      import org.jboss.msc.service.StartException;
      import org.jboss.msc.service.StopContext;
      
      
      /**
       * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
       */
      public class HATimerService implements Service<String> {
          private static final Logger LOGGER = Logger.getLogger(HATimerService.class);
          public static final ServiceName SINGLETON_SERVICE_NAME = ServiceName.JBOSS.append("quickstart", "ha", "singleton", "timer");
      
          /**
           * A flag whether the service is started.
           */
          private final AtomicBoolean started = new AtomicBoolean(false);
      
          /**
           * @return the name of the server node
           */
          public String getValue() throws IllegalStateException, IllegalArgumentException {
              LOGGER.infof("%s is %s at %s", HATimerService.class.getSimpleName(), (started.get() ? "started" : "not started"), System.getProperty("jboss.node.name"));
              return "";
          }
      
          public void start(StartContext arg0) throws StartException {
              if (!started.compareAndSet(false, true)) {
                  throw new StartException("The service is still started!");
              }
              LOGGER.info("Start HASingleton timer service '" + this.getClass().getName() + "'");
      
              final String node = System.getProperty("jboss.node.name");
              try {
                  InitialContext ic = new InitialContext();
                  ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).initialize("HASingleton timer @" + node + " " + new Date());
              } catch (NamingException e) {
                  throw new StartException("Could not initialize timer", e);
              }
          }
      
          public void stop(StopContext arg0) {
              if (!started.compareAndSet(true, false)) {
                  LOGGER.warn("The service '" + this.getClass().getName() + "' is not active!");
              } else {
                  LOGGER.info("Stop HASingleton timer service '" + this.getClass().getName() + "'");
                  try {
                      InitialContext ic = new InitialContext();
                      ((Scheduler) ic.lookup("global/jboss-cluster-ha-singleton-service/SchedulerBean!org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.Scheduler")).stop();
                  } catch (NamingException e) {
                      LOGGER.error("Could not stop timer", e);
                  }
              }
          }
      }
      
    2. Créer un activateur qui installe le Service en tant que singleton clusterisé.

      La liste suivante est un exemple d'activateur de service qui installe le HATimerService comme service singleton clusterisé :
      package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
      
      import org.jboss.as.clustering.singleton.SingletonService;
      import org.jboss.logging.Logger;
      import org.jboss.msc.service.DelegatingServiceContainer;
      import org.jboss.msc.service.ServiceActivator;
      import org.jboss.msc.service.ServiceActivatorContext;
      import org.jboss.msc.service.ServiceController;
      
      
      /**
       * Service activator that installs the HATimerService as a clustered singleton service
       * during deployment.
       *
       * @author Paul Ferraro
       */
      public class HATimerServiceActivator implements ServiceActivator {
          private final Logger log = Logger.getLogger(this.getClass());
      
          @Override
          public void activate(ServiceActivatorContext context) {
              log.info("HATimerService will be installed!");
      
              HATimerService service = new HATimerService();
              SingletonService<String> singleton = new SingletonService<String>(service, HATimerService.SINGLETON_SERVICE_NAME);
              /*
               * To pass a chain of election policies to the singleton, for example, 
               * to tell JGroups to prefer running the singleton on a node with a
               * particular name, uncomment the following line:
               */
              // singleton.setElectionPolicy(new PreferredSingletonElectionPolicy(new SimpleSingletonElectionPolicy(), new NamePreference("node2/cluster")));
      
              singleton.build(new DelegatingServiceContainer(context.getServiceTarget(), context.getServiceRegistry()))
                      .setInitialMode(ServiceController.Mode.ACTIVE)
                      .install()
              ;
          }
      }
      

      Note

      L'exemple de code ci-dessus utilise une classe, org.jboss.as.clustering.singleton.SingletonService, qui fait partie de l'API privée de JBoss EAP. Une API publique deviendra disponible dans la version EAP 7 et les classes privées seront obsolètes, mais ces classes seront entretenues et rendues disponibles pendant toute la durée du cycle de version EAP 6.x.
    3. Créer un fichier ServiceActivator

      Créer un fichier nommé org.jboss.msc.service.ServiceActivator dans le répertoire resources/META-INF/services/ de l'application. Ajouter une ligne contenant le nom complet de la classe de Service Activator créée dans l'étape précédente.
      org.jboss.as.quickstarts.cluster.hasingleton.service.ejb.HATimerServiceActivator 
    4. Créer un bean singleton qui implémente une minuterie de singleton à utiliser dans tout le cluster.

      Ce bean Singleton ne doit pas avoir d'interface distante et ne doit pas référencer son interface locale à partir d'un autre EJB d'application. Cela évite une recherche client ou de la part d'un autre composant et assure un parfait contrôle du Singleton par le SingletonService.
      1. Créer une interface d'ordonnancement

        package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
        
        /**
         * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
         */
        public interface Scheduler {
        
            void initialize(String info);
        
            void stop();
        
        }
        
      2. Créer le bean Singleton qui implémente la minuterie de singleton à utiliser dans tout le cluster.

        package org.jboss.as.quickstarts.cluster.hasingleton.service.ejb;
        
        import javax.annotation.Resource;
        import javax.ejb.ScheduleExpression;
        import javax.ejb.Singleton;
        import javax.ejb.Timeout;
        import javax.ejb.Timer;
        import javax.ejb.TimerConfig;
        import javax.ejb.TimerService;
        
        import org.jboss.logging.Logger;
        
        
        /**
         * A simple example to demonstrate a implementation of a cluster-wide singleton timer.
         *
         * @author <a href="mailto:wfink@redhat.com">Wolf-Dieter Fink</a>
         */
        @Singleton
        public class SchedulerBean implements Scheduler {
            private static Logger LOGGER = Logger.getLogger(SchedulerBean.class);
            @Resource
            private TimerService timerService;
        
            @Timeout
            public void scheduler(Timer timer) {
                LOGGER.info("HASingletonTimer: Info=" + timer.getInfo());
            }
        
            @Override
            public void initialize(String info) {
                ScheduleExpression sexpr = new ScheduleExpression();
                // set schedule to every 10 seconds for demonstration
                sexpr.hour("*").minute("*").second("0/10");
                // persistent must be false because the timer is started by the HASingleton service
                timerService.createCalendarTimer(sexpr, new TimerConfig(info, false));
            }
        
            @Override
            public void stop() {
                LOGGER.info("Stop all existing HASingleton timers");
                for (Timer timer : timerService.getTimers()) {
                    LOGGER.trace("Stop HASingleton timer: " + timer.getInfo());
                    timer.cancel();
                }
            }
        }
        
  2. Démarrez chaque instance de JBoss EAP 6 avec le clustering activé.

    Pour activer le clustering dans les serveurs autonomes, démarrer chaque serveur par le profil HA, en utilisant un nom de code unique et un offset (décallage) de port pour chaque instance comme suit :
    • Dans Linux, on utilise la commande suivante pour démarrer les serveurs :
      EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET

      Exemple 9.4. Démarrage de serveurs multiples autonomes dans Linux

      $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node1
      $ EAP_HOME/bin/standalone.sh --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100
    • Dans Microsoft Windows, on utilise la commande suivante pour démarrer les serveurs :
      EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=UNIQUE_NODE_NAME -Djboss.socket.binding.port-offset=PORT_OFFSET

      Exemple 9.5. Démarrage de serveurs multiples autonomes dans Microsoft Windows

      C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node1
      C:> EAP_HOME\bin\standalone.bat --server-config=standalone-ha.xml -Djboss.node.name=node2 -Djboss.socket.binding.port-offset=100

    Note

    Si vous ne souhaitez pas utiliser des arguments en ligne de commande, vous pouvez configurer le fichier standalone-ha.xml pour que chaque instance de serveur puisse se lier à une interface séparée.
  3. Déployer l'application dans les serveurs

    La commande Maven suivante déploiera votre application dans un serveur autonome qui exécute sur les ports par défaut.
    mvn clean install jboss-as:deploy
    Pour déployer des serveurs supplémentaires, indiquer le nom du serveur. S'il est sur un hôte différent, passez le nom d'hôte et le numéro de port dans la ligne de commande :
    mvn clean package jboss-as:deploy -Djboss-as.hostname=localhost -Djboss-as.port=10099
    Voir le quickstart cluster-ha-singleton fourni dans JBoss EAP 6 pour la configuration et les détails de déploiement de Maven.