Pour savoir où on va, il faut savoir d'où l'on vient

Vous avez
une question ?
Un projet ?

Contactez nous !
 

Contactez-nous

Vous avez une question ? un projet ? 
Vous souhaitez plus d'informations sur un produit ? sur notre offre ? 
Contactez-nous, on vous répond sous 4H.

retour

Caractéristiques principales des MOM

Caractéristiques principales des MOM

Nous parcourons ici les principales classes de fonctionnalités offertes par les MOMs, en identifiant les possibilités communes à tous les outils, et celles qui sont plus spécifiques.

Langages d'implémentation ,APIs et environnements supportés

Les MOMs open source que nous étudions ici sont tous codés en Java. Nous ne les avons pas sélectionnés sur ce critère, mais il se trouve que tous les éditeurs concernés ont fait ce choix. Il est assez naturel puisque le MOM doit souvent s’insérer dans un environnement hétérogène, en termes de systèmes d’exploitation et de serveurs. La portabilité est donc primordiale, et elle est l’un des atouts majeurs de l’environnement Java. S’ajoute à cela, la disponibilité dans cet environnement de librairies puissantes et éprouvées, pour les fonctionnalités fondamentales en matière de réseau, de sécurité, d’accès à des bases de données, de gestion transactionnelle, etc.

Cela dit, le langage dans lequel le MOM lui-même est codé pourrait être d’une importance secondaire. De même qu’il importe peu de savoir dans quel langage MySql est codé, du moment que nous pouvons en invoquer les fonctionnalités depuis divers environnements. Ce qui importe pour les applications, c’est la disponibilité d’APIs, de fonctions ou méthodes qui peuvent être appelées pour invoquer les services du MOM.

Mais certains MOMs se sont largement focalisés sur l’environnement Java, y compris pour les APIs, c'est-à-dire qu’ils n’offrent pas d’APIs pour d’autres environnements. C’est, selon nous, un handicap majeur, car la capacité à relier des applications diverses, à gérer l’ hétérogénéité, est précisément une des finalités du MOM. S’il ne peut être mis en œuvre qu’entre des applications Java, il perd une partie de son utilité.

Lorsque le MOM offre des APIs pour d’autres environnements que Java, elles se présentent sous la forme de librairies de fonctions dans l’environnement cible, par exemple en C ou en PHP.

La figure suivante permet de bien distinguer ces notions:

  • L'API proprement dite, qui est l'interface appelée par l'application.
  • Les librairies du provider, invoquées par cet API, représentées ci-dessous en tant que « JMS Provider API »
  • Le Broker, qui est un processus indépendant de l'application, en charge de la gestion des messages.

Les fonctions de la librairie JMS échangent avec le broker par un protocole réseau.

echange_jms_broker

L'échange peut impliquer plusieurs brokers, qui échangent entre eux. Le protocole interne du MOM, entre brokers, peut être le même, ou bien différer du protocole externe.

echange_entre_plus_broker

Rappelons que, par définition, JMS est une API pour l'environnement Java. Dans les exemples précédents, les applications sont donc nécessairement Java.

Si le protocole d'échange avec le broker est standard, une application peut, théoriquement, échanger directement avec le broker, sans passer par une librairie de fonctions. Il suffit qu’elle respecte le protocole d’échange avec le broker. Mais mettre en œuvre un protocole réseau est assez complexe, et source d’erreurs, de sorte que ce n’est pas le rôle d’une application en général.

environnement_hetorogene

Sur l’exemple ci-dessus, on est en environnement hétérogène : certaines applications invoquent le MOM via les APIs fournies, tandis que l’application bleue échange directement avec le broker selon le protocole réseau.

Des APIs peuvent être fournies pour d’autres environnements que JEE, par exemple C++, PHP, .Net, Ruby, Perl. Plus la liste de langages grâce auxquels on peut accéder au MOM est grande, meilleures sont les possibilités d'intégration.

Protocoles

Lorsqu’une application appelle une API pour invoquer le MOM, la fonction d’API prend en charge l’échange avec un broker du MOM.

L’échange entre l’application et le broker implique un protocole. Le protocole définit comment les services du MOM seront spécifiés, leurs paramètres, et le format des messages. Par exemple, le protocole doit spécifier que le nom d’une queue de message est représenté par une chaîne de caractères codés en UTF8.

On peut distinguer des protocoles externes, entre application et brokers, et des protocoles internes, entre brokers.

Il existe deux standards en matière de protocole MOM : AMQP (Advanced Message Queuing Protocol) et STOMP. On les appelle des « wire-level protocols » (protocoles filaires), dans le sens où ils sont en charge de gérer les échanges sous la forme d’une suite d’octets transmis.

Comme toujours en matière de communication réseau, on a affaire à une pile de protocoles, c'est-à-dire que le protocole du MOM s’appuie lui-même sur des couches de protocoles inférieures. Ainsi, STOMP peut s’appuyer, à la manière du HTTP, sur une pile TCP/IP. On appelle support de communication logique le protocole de transmission du message, par exemple STOMP dans l’exemple précédent.

protocoles

Le schéma précédent fait apparaître un exemple de pile de protocoles. Les différentes flèches horizontales représentent les échanges virtuels, aux différents niveaux : au niveau le plus haut, une application échange avec une autre, en fait le broker d’un JMS provider échange avec son homologue. Les messages descendent puis remontent la pile des protocoles, comme classiquement.

Notons que du côté des MOMs, on parle souvent de « connecteurs » pour parler des différents protocoles.

Traitement des messages par les MOM

La fonction naturelle, essentielle, d'un MOM n’est pas d'effectuer des traitements sur les messages qui lui sont confiés. Sa fonction est de les acheminer de manière fiable jusqu'à leur destinataire. C'est même ce qui distingue le MOM d'un EAI ou bien d'un ESB: il achemine les messages et c'est tout. En particulier, le MOM ne « regarde » pas le contenu des messages, ce n'est pas son problème.

Pourtant, l'un des MOMs que nous étudierons, ActiveMQ, offre cette possibilité supplémentaire, de définir des traitements à exécuter sur les messages qui lui sont confiés. Ces traitements sont définis en référence aux différents Enterprise Integration Patterns, un recensement des familles de traitements (cf « Enterprise Integration Patterns », page 35.

Un cas simple, par exemple, est un traitement d'aiguillage, en fonction du contenu du message: le message concerne sur des ordres de bourse, si l'ordre porte sur une valeur EuroNext, il doit être routé sur une queue A, s'il porte sur une valeur du NYSE, il doit être routé sur une queue B.

Un autre exemple serait une règle d'envoi d'une copie: si le montant de l'ordre de bourse est supérieur à 1 million, alors il faut envoyer un message en copie sur une queue C.

La question importante est: Est-ce une bonne idée d'insérer ces règles et ces traitements dans le MOM ? Ne sont-ils pas plutôt du ressort de l'application ? Le MOM ne devrait-il pas plutôt rester dans son rôle de tuyauterie passive ?

La réponse n'est pas immédiate. Sortir certaines règles des applications peut être un moyen de gagner en flexibilité, d'intervenir dans la gestion des flux sans modifier les applications. Mais si l'on met en œuvre de tels traitements de manière massive, alors on a en fait éparpillé des morceaux d'applications dans le middleware, et cela au détriment de la maintenabilité, et de la cohérence de vision.

Quoi qu'il en soit, si le MOM n'offre pas de telles possibilités, ou bien qu'on ne veut pas en faire usage, il est toujours possible, et même aisé, de les mettre en place dans des applications relais.

Gestion des transactions

On peut distinguer trois niveaux dans la gestion transactionnelle des messages:

  • La gestion des acquittements
  • La gestion des transactions JMS
  • La gestion des transactions XA

L'application destinataire, qui consomme les messages, doit généralement effectuer un traitement qui dépend de ce message. Le message ne doit donc pas seulement être lu, il doit être traité. C'est une distinction importante, dans la mesure où l'application pourrait s'arrêter brutalement (bug ou bien panne matérielle) entre l'instant où elle a lu le message et l'instant où elle a fini de le traiter avec succès.

C'est pourquoi le fonctionnement normal de l'application consommatrice est en trois étapes:

  1. Recevoir un message
  2. Traiter le message
  3. Acquitter le message, c'est-à-dire notifier la bonne fin du traitement.

Tant que le message n'a pas été acquitté, il est conservé par le broker. Si le message n'est jamais acquitté, il est recyclé, c'est-à-dire qu'il sera remis lors d'un prochain appel d'une application cliente. Notons que c'est ce principe qui rend presque impossible la garantie de délivrance ordonnée pour les MOMs en général.

Une application cliente peut acquitter en un seul appel, tous les messages reçus et encore non acquittés. C'est donc une forme de gestion transactionnelle en lecture.

Transactions JMS

Il est possible de réunir différents ordres d'émission et de réception de messages en une transaction, un ensemble insécable d'opérations. C'est-à-dire que soit toutes ces opérations seront exécutées avec succès, soit aucune d'entre elles ne sera exécutée.

Comme pour les bases de données, l'application ouvre une transaction, effectue différentes opérations JMS, puis termine la transaction par un ordre commit. Si l'application détecte une condition d'erreur qui interdit de terminer avec succès l'ensemble des opérations, elle demande un rollback, c'est-à-dire un retour arrière sur toutes les opérations précédentes. Si l'application « se plante », et donc s'interrompt sans avoir fait ni commit, ni rollback, un rollback sera exécuté de manière implicite. Dans le cas d'émissions de messages, aucun message n'a en fait été émis avant le commit. Dans le cas de réception de messages, aucun acquittement n'aura été exécuté avant le commit.

Il y de nombreux usages de ces transactions JMS, d'une manière générale pour assurer la cohérence:

  • Une application peut par exemple émettre 10 messages et être assurée que soit tous seront bien émis, soit aucun ne le sera.
  • Une application qui jouerait un rôle de relais pourra ainsi lire un message sur une queue, le traiter, et écrire un message résultant sur une queue en aval, tout cela au sein d'une transaction, et donc avec la garantie de ne pas perdre de message si elle est interrompue entre la lecture et l'écriture.
  • Enfin, de la même manière, une application qui doit réceptionner plusieurs messages avant d'effectuer un traitement, peut réunir ces lectures en une même transaction.

    Voici un petit exemple de code Java utilisant les transactions.

     

    Si le traitement réussit, le programme client exécute un commit, sinon, il demande un rollback, c'est-à-dire qu'il ordonne au broker tout annuler.

Transactions XA

Enfin, la troisième manière de gérer les transactions s’inscrit dans le cadre de « XA » en environnement Java. XA est une spécification définissant les interfaces qui permettent de mettre en œuvre des transactions hétérogènes, c'est à dire s'étendant à plusieurs ressources de différentes natures, telles que bases de données, serveurs d'application (EJB), ainsi donc que les MOMs. En environnement Java, XA est disponible via l'API JTA, Java Transaction API.

Il s'agit donc de réunir, dans un même ensemble insécable, indivisible, des traitements portant sur ces diverses ressources.

Un cas très simple et typique est celui d'une application qui:

  • Lit un message auprès d'un broker de MOM
  • Effectue une écriture sur la base de données.

En l'absence de transactions XA, l'application devrait acquitter son message auprès du MOM soit avant, soit après, l'écriture en base. Mais si elle acquitte avant, puis se plante, elle n'a pas effectué l'écriture, mais le message est pourtant considéré traité avec succès. Si à l'inverse elle écrit dans la base en premier, mais se plante avant d'avoir acquitté le message, alors le message sera recyclé, et il y aura donc eu deux écritures.

Sur cet exemple très simple, on voit donc que les transactions XA peuvent être absolument indispensables dans certains contextes afin d'assurer une réelle garantie de cohérence au niveau global du système d'information. Bien entendu, les transactions peuvent être sensiblement plus larges et plus complexes.

Dead Messages Queue

Même si ce n'est pas requis par la spécification JMS, différents MOMs définissent une queue spéciale appelée « Dead Message Queue » ou DMQ, qui correspond à une sorte de poubelle, où l'on pourra retrouver des messages qui auraient pu être perdus pour différentes raisons techniques.

Généralement, la DMQ reçoit les messages :

  • qui n'ont pas une destination valide.
  • dont la destination est remplie. (limite, plus de mémoire, ...)
  • dont la durée de vie (TTL, time to live) a expiré
  • qui se sont fait rejeter un certain nombre de fois (configurable). Ces messages apparaissent comme des messages « poisons » polluant la plateforme. Par exemple, un message qui fait planter systématiquement un client est un message polluant.

Bien sûr, il convient qu'un administrateur analyse ces messages pour en déterminer les causes d'erreur éventuelles. La DMQ contribue à garantir qu'aucun message n'est perdu par le MOM, il est donc naturellement recommandé qu'elle soit persistante.

Persistance des messages

Comme on l’a vu en introduction, la fiabilité et la robustesse sont deux qualités essentielles, constitutives des MOMs, c'est-à-dire qu’un MOM doit acheminer un message qui lui a été confié, sans jamais le perdre, même en présence d’événements inattendus.

Si l’application destinatrice n’est pas en mesure de recevoir le message, le MOM peut être amené à le conserver un temps indéfini. Or le MOM lui-même peut être arrêté, que ce soit du fait de pannes matérielles ou pour des raisons de maintenance.

Pour garantir que les messages ne seront pas perdus, le MOM doit donc les stocker de manière sécurisée, de manière persistante.

Il est possible de faire fonctionner un MOM dans un mode sans persistance, c'est-à-dire dans un mode où les messages sont seulement conservés en mémoire. On peut choisir ce mode pour atteindre des performances plus élevées – car la persistance a un coût – au détriment bien sûr de la fiabilité.

La persistance est toutefois importante, voire essentielle, dans les cas suivants :

  • Lorsque les messages sont critiques, par exemple s’il s’agit de transactions financières.
  • S’il peut y avoir un déséquilibre positif entre producteurs et consommateurs, c'est-à-dire que de manière durable les applications productrices émettent plus de messages que les applications destinatrices ne peuvent lire et traiter. Les capacités mémoires risqueraient d’être dépassées.
  • Lorsque le traitement des messages est fortement asynchrone, de manière structurelle, c'est-à-dire par exemple si les messages ne sont traités qu’en fin de journée, d’une manière que l’on pourrait assimiler à un traitement batch.
  • Lorsque l’on doit mettre en œuvre une gestion des transactions, qui implique une utilisation plus importante de la mémoire. Les messages utilisant les transactions ne sont supprimés que lorsque les transactions sont validées.
  • En présence de réplication, lorsqu’elle est offerte. Il est nécessaire de mettre en œuvre la persistance pour accroître les possibilités de stockage : un broker ne pourra gérer la réplication de tous les domaines de la plateforme MOM en mémoire.

La persistance peut être mise en œuvre par le MOM de différentes manières :

  • Sur de simples fichiers
  • Sur une base de données relationnelle
  • Au moyen d’un dispositif spécifique combinant deux supports de persistance.

Le stockage sécurisé des données et leur gestion transactionnelle étant un problème déjà parfaitement résolu par les SGBD relationnels, la plupart des MOMs appuient leur persistance sur une telle base. L’accès par JDBC leur permet de supporter un large éventail de gestionnaire de base de données (Mysql, Postgres, Oracle, DB2, ...), y compris des bases 100% java telles que Hypersonic et Derby.

Tous les MOMs étudiés ici supportent la persistance via JDBC. Cependant, chaque MOM stocke différemment les données. Certains introduisent un mode de persistance optimisé. Ils sont amenés parfois à combiner trois types de stockage : fichier, base de données et mémoire. Et ceci dans le but d'optimiser la fiabilité et la performance. C’est donc un aspect que nous développerons pour chacun des outils, et le dernier chapitre présentera les résultats de différents tests de performance.