ActiveMQ et architecture haute disponibilité

L’échange de messages entre différents systèmes peut être mis en oeuvre par le bus Apache ActiveMQ, déployé avec la solution Talend ESB. L’objectif de cet article est de faire les recommandations nécessaires à la mise en oeuvre d’une architecture haute disponibilité reposant sur le bus ActiveMQ.

L'environnement technique pris comme exemple est le suivant :

  • Talend ESB 5.3.1
  • ActiveMQ 5.7
  • Camel 2.11

Haute Disponibilité ActiveMQ

Présentation

La Haute Disponibilité (High Availability) permet d’assurer la continuité de service en cas de défaillance d’un ou plusieurs composants opérationnels. Pour la HA reposant sur ESB, on distingue :

Haute disponibilité : fonctionnement de plusieurs brokers Actif/Actif en réseau sur au moins deux sites. Dans le cas où un broker est défaillant, les autres brokers traitent les nouveaux messages. En revanche, les messages qui étaient en cours de traitement sur le broker défaillant y restent bloqués jusqu’à ce qu’il soit relancé.

Très Haute Disponibilité : fonctionnement similaire mais chacun des brokers actifs dispose d’au moins un backup (broker passif) sur le même site, ce qui permet la reprise immédiate des messages en cours de traitement (les messages ne restent pas bloqués en attendant la relance du broker défaillant).

Full Disaster Recovery : c’est le complément de la très haute disponibilité. Le full DR assure qu’en cas d’incident majeur (perte d’un site de production), les messages en cours de traitement sont repris sur le site distant (ceci induit un mode de réplication du message store des brokers entre les sites).

Stratégie HA : 5 points clés

Réseau de brokers actifs/actifs : chaque broker ActiveMQ est actif et connecté aux autres dans un mode « store and forward ». Ceci signifie qu’un message  est pris en charge par un et un seul broker à tout instant. Il existe plusieurs typologies de réseau de brokers, de la plus minimale (deux brokers) au « Hub and poke » en passant par le réseau de type « Concentrateur ». ActiveMQ route les messages entre producers et consumers quelque soit leur localisation.

Backup des brokers : ActiveMQ permet la très haute disponibilité grâce à une configuration Master/Slave de chacun des brokers actifs. Lorsque le broker nominal maître tombe, un des brokers esclave prend aussitôt le relais en toute transparence (réplication du message store et génération des destinations  JMS).

Fail-over clients : ActiveMQ autorise une connexion des clients (producers et consumers) avec fail-over automatique pour leur permettre de basculer vers un broker disponible du réseau lorsque leur broker serveur est défaillant. Gestion des transactions JMS : ActiveMQ gère les transactions JMS et permet de paramétrer les stratégies de rejeu des messages (redelivery et DLQ).

Persistance : ActiveMQ permet l’activation de la persistance des messages lorsque ceux-ci ne doivent pas être perdus en cas d’incident.

Exemple d'architecture

ActiveMQ failover

Cette architecture minimaliste permet :

  • Le traitement des messages d’information sur 2 brokers (activemq1 & activemq2) actifs répartis
  • Le load balancing des traitements sur site (et entre les deux sites) si les consumers sont concurrents sur chaque queue de message
  • Le failover des clients (producteurs et consommateurs) en cas de défaillance d’un bus (activemq1 dans cet exemple)

En cas de défaillance d’un broker :

  • Les clients (producteurs et consommateurs) doivent basculer sur l’autre broker
  • Le broker actif assure le traitement des nouveaux messages
  • Les messages en cours de traitement sur broker activemq2 sont bloqués et en attente de relance
  • Après relance du broker activemq1 , il faut appliquer une procédure de bascule arrière des clients, sinon ils restent connectés à l’autre site

Configuration des brokers

Mode de configuration : fichier « mon-broker.xml »
Points à configurer :

  • Le réseau de brokers
  • La persistance du broker
  • La stratégie de rejeux des messages transactionnels

Configuration du réseau de brokers

Note : la configuration est identique sur les deux brokers, <nom> désigne une variable.

Sur la balise <broker> paramétrez :

  • brokerName : un nom distinct doit être donné à chacun des brokers
  • advisorySupport : « true » permet l’activation des messages de gestion du réseau de brokers - découverte des brokers, des clients (producteurs, consommateurs) et routage dynamique des messages
<broker xmlns=« http://activemq.apache.org/schema/core" brokerName="<nom_du_broker>" advisorySupport="true">

Sur la balise <networkConnectors> paramétrez :

  • networkConnector@uri : création d’un canal statique vers l’autre broker
  • networkTTL : nombre de sauts (de brokers) autorisés pour un message (1 minimum)
<networkConnectors><networkConnector uri="static:(tcp://<host_broker_distant>:<port_broker_distant>)" networkTTL="1"/></networkConnectors>

Sur la balise <transportConnectors> paramétrez :

  • transportConnector@uri : création d’un canal vers le broker
<transportConnectors><transportConnector name="openwire" uri="tcp://<host_broker_local>:<port_broker_local>" /></transportConnectors>

Configuration de la persistance

Note : pour plus de simplicité, la configuration est identique sur les deux brokers.

Sur la balise <broker> paramétrez :

  • persistent : « true » indique à ActiveMQ d’activer la persistance sur disque et non en mémoire
<broker xmlns=« http://activemq.apache.org/schema/core" persistent="true">

Sur la balise <persistenceAdapter> paramétrez :

  • kahaDB@directory : le chemin vers le message store généré par le journal de persistance KahaDB (module embarqué dans ActiveMQ)
<persistenceAdapter><kahaDB directory="${activemq.data}/<nom_du_broker>/kahadb"/></persistenceAdapter>

Remarque : Du fait de la norme JMS, les messages sont publiés en mode persistant par défaut mais il est plus lisible de le préciser explicitement via l’API du producer JMS.

producer.setDeliveryMode(DeliveryMode.PERSISTENT | NON_PERSISTENT);

Stratégies de rejeux transactionnels

Stratégie de rejeu (redelivery)

maximumRedeliveries : nombre de tentatives si le consommateur du message échoue.
redeliveryDelay : (en ms) délai avant re-livraison du message.

La stratégie est applicable à chaque queue en particulier ou à toutes, par défaut.

<plugins>
   <!-- demande l’envoi en DLQ lorsque toutes les tentatives de rejet sont épuisées -->
   <redeliveryPlugin fallbackToDeadLetter="true" sendToDlqIfMaxRetriesExceeded="true">
      <redeliveryPolicyMap>
         <redeliveryPolicyMap>
            <redeliveryPolicyEntries>
               <!-- a destination specific policy -->
               <redeliveryPolicy queue="nom_de_queue" maximumRedeliveries="4" redeliveryDelay="10000" />
            </redeliveryPolicyEntries>
            <!-- the fallback policy for all other destinations -->
            <defaultEntry>
               <redeliveryPolicy maximumRedeliveries="4" initialRedeliveryDelay="5000" redeliveryDelay="10000" />
            </defaultEntry>
         </redeliveryPolicyMap>
      </redeliveryPolicyMap>
   </redeliveryPlugin>
</plugins>

Stratégie de rejet (DLQ)

Sont déversés sur Dead Letter Queue, tous les messages :

  • qui ont expirés (propriété JMS time-to-live du message)
  • dont les tentatives de rejet sont épuisées

Sur la balise <destinationPolicy> il est possible d’indiquer comment générer automatiquement le nom des Dead Letter Queue via queuePrefix. Ce type de stratégie est possible pour les Topics.

<destinationPolicy>
   <policyMap>
      <policyEntries>
         <policyEntry queue=">" producerFlowControl="true" memoryLimit="20mb">
            <deadLetterStrategy>
               <individualDeadLetterStrategy queuePrefix="DLQ." useQueueForQueueMessages="true" />
            </deadLetterStrategy>
         </policyEntry>
         <policyEntry topic=">" producerFlowControl="true" memoryLimit="20mb" />
      </policyEntries>
   </policyMap>
</destinationPolicy>

Important: un service dédié à la gestion des messages rejetés doit s’abonner aux DLQ pour les rejouer ou les purger selon le cas.

Configuration des clients

Mode de configuration : via l’API JMS ou Camel.
Points à configurer :

  • la stratégie de fail-over des clients (producteurs et consommateurs)
  • la gestion des transactions JMS

Stratégies de fail-over des clients

Failover des clients : les producteurs et consommateurs JMS peuvent être configurés pour basculer automatiquement d’un broker à l’autre en cas de besoin.

Il est possible de prioriser la connexion au broker local. Pour cela, ActiveMQ permet de configurer dans l’url de connexion au broker la liste des brokers de secours à utiliser.

String url = "failover:(tcp://localhost:61618,tcp://localhost:61616)?randomize=false&maxReconnectAttempts=10&initialReconnectDelay=100&priorityBackup=true"
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);

randomize=false et priorityBackup=true : favorisent le premier broker (local)
maxReconnectAttempts=10 : nombre de tentatives de reconnexion
initialReconnectDelay=100 : délai en millisecondes avant tentative

Gestion des transaction JMS

La gestion des transactions JMS est indispensable pour ne pas perdre de messages. Un échange transactionnel concerne le producteur comme le consommateur.

session = connection.createSession(true, Session.SESSION_TRANSACTED);
session = connection.createSession(true, Session.CLIENT_ACKNOWLEDGE);

Session JMS transactionnelle : la session JMS doit être ouverte en mode transactionnel via l’API JMS ou Camel.

Il existe deux modes :

  • la transaction au niveau de la session (SESSION_TRANSACTED) s’applique à tous les consommateurs ou producteurs de la session (et tous les messages consommés ou émis)
  • la transaction au niveau du message (CLIENT_ACKNOWLEDGE) s’applique à tous les messages qui n’ont pas encore été acquittés ou rollbackés

Par défaut, utiliser SESSION_TRANSACTED si un seul producteur ou consommateur est déclaré par session.

Note : Camel utilise la notion de SESSION_TRANSACTED par défaut lorsque le mode transactionnel est activé.

Une fois la session rendue transactionnelle, le consommateur doit gérer les acquittements sur les messages via les notions de commit / rollback.

Si la session est SESSION_TRANSACTED, utilisez :

session.commit() / session.rollback()

Si la session est CLIENT_ACKNOWLEDGE, utilisez :

message.acknowledge() / session.recover()

N.B. Quand rollbacker un message ?

Erreur temporaire : en cas d’erreur temporaire (base de données indisponible, problème de connexion, etc.), le message peut être rollbacké sur le bus et sera consommé par un autre consommateur ou le même s’il est à nouveau opérationnel (il y a temporisation).

Erreur permanente : (erreur de parsing) dans ce cas il ne faut pas rollbacker le message (risque de saturation en boucle du broker) mais le traiter à part.

Autres bonnes pratiques

Durée de vie des messages : il est important de paramétrer une durée de vie limitée des messages (infinie par défaut) car dans le cas où la volumétrie augmenterait brusquement, il existe un risque d’engorgement des queues et topics. Les messages expirés peuvent être envoyés en DLQ pour être rejoués de manière différée si le cas métier le permet.

Traitements concurrents : lorsque les messages ne sont pas liés entre eux, il est possible de les traiter de manière concurrente (ajout de consommateurs) pour augmenter les performances. Dans ce cas le réseau ActiveMQ distribue automatiquement la charge.

Traitements ordonnés : lorsque les messages sont liés entre eux (« début de l’incident de circulation X », « fin de l’incident de circulation X ») il doivent être traités en séquence via un seul consommateur (ce qui peut réduire les performances), sauf si la publication de ces messages est suffisamment espacée dans le temps et évite le risque désordre.

Traitement ordonnées distribués : pour palier à la limite précédente et traiter en concurrence les messages tout en conservant leur ordre logique, il est possible de les publier sur un mode « sticky session » via le header JMS « JMSXGroupID ». De cette manière, tous les messages concernant « l’incident de circulation X » pourront être traités par le même consommateur (choisi dynamiquement par le réseau de brokers). L’ordre des messages est garanti s’il n’y a pas de bond entre brokers.


					

Un commentaire

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.