Nous avons choisi d'évaluer les solutions suivantes :

  1. ActiveMQ, broker JMS apache
  2. RabbitMQ, broker AMQP en erlang
  3. Qpid, autre boker AMQP apache

Nos spikes doivent être fonctionnels, et pouvoir être testés en charge, avec une architecture assez proche de ce que nous avons en production. Comme le service est un service web, et que les statistiques sont émises dans des servlets, nous choisissons de réaliser une petite servlet qui envoie un objet représentant une statistique sur le bus de données, et un consommateur qui reçoit cet objet.

Voici l'architecture de la plateforme de bench nous servant à tester nos spikes :

Architecture des tests de charge

Nous avons défini des catégories de critères :

  • fonctionnels : par exemple, pas d'impact sur les performances des AS, pas de perte de messages, ne pas avoir des messages traités 2 fois même avec plusieurs consommateurs, un temps d'insertion en base inférieur à 10s
  • tenue en charge : 10 Millions de messages par jour, environ 120 messages par seconde
  • techniques : monitoring, logging, installation, facilité d'utilisation des API, adhérence et intrusivité dans le code, qualité de la documentation

Pour chaque solution, nous cheminons de manière incrémentale :

  • installer les solutions sur nos poste pour pouvoir les utiliser en local
  • d'abord implémenter un petit programme producteur/consommateur (deux classes main java) et voir si l'objet représentant un message de statistique qui contient des attributs de différents types est bien reçu et décodé
  • ensuite coder une servlet qui va utiliser le producteur et envoyer un message en dur dans sa méthode doGet. Cette étape nous permet de voir comment utiliser l'API d'envoi de message dans un contexte multithreadé. Nous avons déjà du faire un peu de design pour pouvoir nous adapter au contexte d'un container de servlet
  • déployer la solution en plateforme d'intégration, avec une seule machine de bus, en travaillant avec l'équipe système. Cette étape nous permet d'évaluer l'exploitabilité de chacune des solutions
  • déployer la solution en plateforme de bench, avec un cluster de deux machines de bus, toujours avec l'équipe système. Nous pouvons évaluer la difficulté de mise en œuvre du cluster
  • faire une petite campagne de bench, afin d'avoir des tirs reproductibles que nous savons expliquer.
  • finaliser la synthèse de toutes les observations recueillies au fil de l'eau pour chaque solution, et capitaliser les connaissances acquises : configurations, contournements éventuels, lecture des logs applicatives, etc.

Nous avons sous-traité la solution Qpid à une autre équipe, car nous ne pouvions pas réaliser les trois spikes dans le temps imparti. Assez rapidement, le développeur qui utilisait Qpid nous a remonté des difficultés importantes de stabilité du broker : régressions entre différentes versions, difficultés de mise en oeuvre. Nous avons décidé en équipe d'abandonner Qpid.

Restaient RabbitMQ et ActiveMQ. Deux parties de l'équipe ont réalisé de manière simultanée ces spikes et cela à amené une compétition saine entre les différentes parties prenantes de chacune des solutions. Régulièrement, lors de nos "dailies", nous échangions autour des difficultés rencontrées par chacune des équipes. Nous avons souhaité arriver au terme des tests de charge dans des conditions similaires, pour pouvoir comparer le plus objectivement possible. Il y avait du challenge, mais aussi de l'entraide.

Après deux semaines de tests, nous sommes arrivé au terme de notre campagne de spike avec le tableau de synthèse suivant :

+/- ActiveMQ +/- RabbitMQ
+ installation plus simple - RabbitMQ installé à partir tar.gz car .deb dans testing
+ paramétrage plus complet + paramétrage plus simple
+ monitoring JMX monitoring peut être scripté sur rabbitmqctl ou par le plugin SNMP
+ logs java par log4j - logs local7 à paramétrer/développer
+ docs + complète - docs moins complète
- comportement en bench difficile à expliquer, plus de temps passé sur les tirs pour arriver au résultat. Suivant la configuration, influence des consommateurs sur les producteurs ++ bench prédictibles et expliquables
- nombre de lignes de code pour implémenter le spike + important (559 lignes java, 478 lignes XML) + nombre de lignes plus faible (347 lignes java)
- couplage à une API/Framework : JMS activeMQ + AMQP : Advanced Message Queuing Protocol, protocole interopérable niveau réseau permet d'ouvrir l'API du bus
+ comportement fonctionnel en charge ++ asynchronisme obtenu sans efforts
+ utilisation répandue + utilisation répandue. Bâti sur erlang qui a été fait pour faire tourner des programmes sans interruption
- nombre de lignes de code du produit activemq-core=158383 (SLOC activemq-parent-5.3.2) + cf http://www.rabbitmq.com/resources/RabbitMQ_FITEclub_2009.pdf : ~5000 lignes de code. Cela entraîne une réduction du risque de défauts
+ possibilité de modifier la configuration, et le code à chaud. Possibilité de se connecter à chaud sur la VM erlang et d'afficher les flux de données "en live". Pas nécessaire de modifier des fichiers de configuration, de redémarrer le service pour changer par exemple le niveau de log.

Le choix est éclairé par les atteintes des objectifs fixés avant la campagne de prototypage, et la confiance de l'équipe dans la solution.

Nous avons organisé une réunion commune avec l'équipe système, durant laquelle nous avons parcouru les points positifs et négatifs de chacune des solutions du tableau ci-dessus. Puis nous avons décidé du choix ensemble. Comme nous avions 10 points positifs pour rabbitMQ contre 7 points positifs pour activeMQ, nous avons opté pour rabbitMQ. Il y a eu un decider et nous avons eu la plupart de l'équipe d'accord, quelques supports, et un non (dans l'équipe système).

Maintenant que le choix était fait, il fallait implémenter la solution et passer l'épreuve de la production : nous en parlerons dans d'autres posts à venir...