Créer un cluster ActiveMQ hautement disponible

le 24/08/2011 par David Rousselie
Tags: Software Engineering

La tendance des bus de messagerie est aujourd'hui de proposer des modes de déploiement distribués au delà de l'architecture master/slave qui se veulent simple à mettre en oeuvre et dynamique. ActiveMQ n'est pas en reste et offre la possibilité de créer un cluster de brokers qui savent intégrer un nouveau broker et détecter la perte d'un broker. Ces informations peuvent ensuite être transmises aux clients connectés pour se reconnecter sur un autre broker du cluster pour mieux répartir la charge ou tout simplement car ils étaient connectés à un broker qui vient de tomber.

Nous verrons comment ces fonctionnalités permettent d'obtenir un système tolérant à la panne et quels sont les limites à prendre en considération.

Voyons tout d'abord la gestion du cluster entre brokers.

Un réseau dynamique de brokers

ActiveMQ possède plusieurs connecteurs de transports. Certains permettent à un broker ActiveMQ de découvrir d'autres brokers ActiveMQ et de joindre un cluster. C'est le cas du transport multicast que nous allons utiliser dans cet article. A noter que c'est également le cas du connecteur ZeroConf. Ainsi, la première étape est de configurer le transport dans le fichier de configuration activemq.xml du broker :

<transportConnectors>
  <transportConnector name="openwire"
                      uri="tcp://0.0.0.0:61616"
                      discoveryUri="multicast://default?group=group_test" />
</transportConnectors>

Avec cette configuration, le broker ActiveMQ sera découvrable sur le réseau dans le groupe group_test. La notion de groupe permet d'isoler différent clusters sur le même LAN.

Maintenant que les brokers du cluster sont découvrables, encore faut-il indiquer à ActiveMQ de former un cluster et de rechercher les autres brokers. Ceci se fait en configurant un connecteur réseau avec une URI de type multicast :

<networkConnectors>
  <networkConnector uri="multicast://default?group=group_test"
                    dynamicOnly="true"
                    networkTTL="3"
                    duplex="true"
                    prefetchSize="1"
                    decreaseNetworkConsumerPriority="false" />
</networkConnectors>

Les paramètres les plus importants sont :

  • networkTTL qui spécifie le nombre de brokers que peut traverser un message pour atteindre un consommateur,
  • prefetchSize qui indique le nombre de messages à récupérer par un broker,
  • decreaseNetworkConsumerPriority qui permet de considérer les consommateurs sur le broker courant avec la même priorité que les consommateurs des autres brokers.

Dans la pratique, lors des tests que j'ai pu faire, j'ai rencontré quelques instabilités dans le système de découverte des brokers qui, de temps en temps, n'arrivent pas à se trouver.

Topologie du réseau de brokers

Après que les brokers se soient découverts les uns les autres, il est possible de répartir les clients consommateurs de messages sur les différents noeuds du cluster. Les producteurs pourront ensuite publier leurs messages sur n'importe quel noeud. En effet, les messages seront transmis aux autres noeuds qui ont des consommateurs pour la queue ou le topic de destination.

Ainsi, lors de la publication d'un message, le broker qui le réceptionne le retransmet aux autres brokers du cluster qui ont des consommateurs et ne répond au producteur que lorsque le message aura été traité (reçu et persisté si nécessaire) par ces brokers. De même, lors de la consommation d'un message sur une queue tous les brokers doivent être notifiés que le message a été consommé pour ne pas être consommé une seconde fois. La latence de publication et de consommation des messages est donc forcément plus importante qu'avec un seul broker.

Propagation d'un message à destination d'un Topic dans un cluster ActiveMQ

Propagation d'un message à destination d'un Topic dans un cluster ActiveMQ

Propagation d'un message à destination d'une Queue dans un cluster ActiveMQ

Propagation d'un message à destination d'une Queue dans un cluster ActiveMQ

Fail-over des brokers

Si un broker qui reçoit des messages du/des producteur(s) quitte le cluster (intentionnellement ou pas), il existe 2 cas :

  • soit il y avait des consommateurs connectés sur d'autres brokers, et les messages auront été transférés à ces brokers et ces messages seront assurés d'être consommés,
  • soit il n'y avait aucun consommateur sur les autres brokers et les messages n'auront donc pas été transmis à ces brokers et seront perdus jusqu'au redémarrage du broker perdu (si les messages ont été persistés bien sûr). Ainsi, lorsque les consommateurs du broker qui a disparu se reconnectent sur un autre noeud du cluster ou que de nouveaux consommateurs se connectent au cluster, ils ne recevront aucun message. Pour contourner ce problème, il existe plusieurs solutions :
    • il est possible de forcer la retransmission des messages entre les noeuds du cluster avec la balise <staticallyIncludedDestinations> (http://activemq.apache.org/networks-of-brokers.html). Il n'est cependant pas possible de configurer le nombre de brokers maximum recevant les messages pour ne pas engorger le réseau et ralentir le cluster.
    • une autre façon de s'assurer de ne pas perdre de messages est de configurer ActiveMQ en mode master/slave avec un système de persistence partagé au niveau du système de fichiers ou de la base de données (via JDBC ou la base KahaDB d'ActiveMQ). Dans ce mode d'utilisation, le nombre de consommateur ne pourra pas scaler de la même façon puisqu'un seul broker sera actif à la fois. Ce qui est tout à fait suffisant dans de nombreux cas.

Fail-over des clients

Depuis les dernières versions d'ActiveMQ, les brokers du cluster transmettent les ajouts et suppressions de brokers aux clients du cluster (producteurs et consommateurs). Ainsi, dès qu'un broker tombe, les clients de celui-ci savent se reconnecter sur un autre broker sans lui avoir spécifié la liste des brokers du cluster dans sa configuration.

Du côté de la configuration des brokers, il faut ajouter les options suivantes :

<transportConnectors>
  <transportConnector name="openwire"
                      uri="tcp://0.0.0.0:61616"
                      discoveryUri="multicast://default?group=group_test"
                      rebalanceClusterClients="true"
                      updateClusterClients="true"
                      updateClusterClientsOnRemove="true" />
</transportConnectors>

updateClusterClients et updateClusterClientsOnRemove activent respectivement la notification des clients lors de l'ajout et de la suppression d'un broker dans le cluster. rebalanceClusterClients essaie de répartir équitablement les clients sur les brokers du cluster. En effet, sans cette option, lorsqu'un nouveau broker joint le cluster, aucun client existant ne se connectera dessus.

Enfin, côté client, celui-ci ne nécessite l'adresse que d'un seul broker pour se connecter la première fois (voir en utilisant une VIP pour s'assurer de se connecter sur un broker accessible du cluster). Ensuite, en utilisant le transport failover d'ActiveMQ, il recevra la liste des autres brokers du cluster. Il sera alors capable de se reconnecter sur un autre broker en cas d'échec du premier mais aussi de se reconnecter pour rebalancer les connexions clientes sur l'ensemble du cluster. Cependant, ce méchanisme de rebalancement des clients reste très primitif puisqu'il n'est pas rare de se retrouver avec des brokers sans consommateur (avec 4 brokers et 8 consommateurs lors de mon test).

Exemple d'URL de connexion cliente :

failover:(tcp://activemq1:61616)

Dans ce cas, le client utilisera le broker activemq1 pour obtenir la topologie du cluster et sera capable de se connecter sur n'importe quel autre broker lorsque activemq1 tombe.

Publication d'un message dans une Queue avant fail-over

Publication d'un message dans une Queue avant fail-over

Consommation d'un message dans une Queue après fail-over

Consommation d'un message dans une Queue après fail-over

Conclusion

Il est donc possible de créer un cluster dynamique qui permet d'ajouter simplement de nouveaux brokers. Le cluster permettra de survivre à la perte d'un broker et de scaler le nombre de consommateurs connectés mais cela au prix d'une latence plus importante lors de la publication et la consommation des messages. Cependant cette topologie ne convient pas à tous les cas d'usage. Pour cela, il existe d'autres topologies de cluster qui seront plus à même de garantir la disponibilité du cluster (master/slave). Il serait même envisageable de créer un cluster intégrant les 2 approches (multi master et master/slave); peut-être pour un prochain article ...