La Haute Disponibilité avec Petals ESB

Je vous propose aujourd’hui d’étudier une configuration Haute Disponibilité avec Petals ESB. Ce sera l’occasion de comparer l’approche distribuée de Petals par rapport à une approche basée sur des clusters. Au passage, je considère que vous avez déjà lu mon précédent article sur le routage applicatif dans Petals. Régulièrement, j’utiliserai l’acronyme HA (High Availability) dans cet article.

Contexte

Nous allons partir sur une configuration classique :

  • Une application qui fournit une fonctionnalité d’un côté.
  • Une application qui de l’autre côté, demande cette fonctionnalité.
  • Et Petals au milieu pour assurer la médiation.

Notre application fournisseur peut être par exemple un EJB, une application légataire, un web service ou une ressource informatique quelconque. L’autre application est un client quelconque.

Quel est l’intérêt d’avoir au Petals au milieu ?

Dans le cas d’un EJB, on peut prendre le cas d’un EJB 2.x que l’on voudrait exposer en service web (soit vous écrivez un client en Java, soit vous passez par le connecteur EJB de Petals, où là, il n’y a aucun code à écrire). Dans le cas d’une application légataire, on pourra utiliser un connecteur dédié, de sorte à pouvoir le réutiliser avec d’autres applications du même type. Dans le cas d’un service web, on peut imaginer le cas d’un service XML-RPC alors que le client ne sait faire que du Document/Literal. Et pour une ressource informatique, l’intérêt sera en général de serviciser cette ressource, c’est-à-dire d’englober l’accès et la manipulation de cette ressource au travers d’un service (au sens SOA du terme). Du coup, on peut raisonner sur la fonctionnalité rendue par le service (fonctionnalité qui se trouve être implémentée grâce à la ressource en question).

Schématiquement, on a donc…

Application client - Petals ESB - Application fournisseur

En termes de déploiement, on a un composant et une service-unit provide pour importer (rendre accessible) l’application fournisseur dans Petals. Et on a un composant et une service-unit consume pour écouter les requêtes de l’application cliente. La question se pose maintenant de savoir comment assurer la haute disponibilité de l’ensemble.

Solution 1 : la spécialisation des noeuds

Cette solution n’est pas la plus intuitive, mais elle fournit un bon rappel des capacités de Petals. Elle est la plus adaptée lorsque l’on veut à la fois :

  • Répliquer des noeuds, c’est-à-dire les fournisseurs et consommateurs de services qui sont dessus.
  • Gérer la répartition de charge, à savoir que certains services sont plus gourmands que d’autres et que l’on ne souhaite pas les voir s’exécuter tous sur la même machine.

Avant même le déploiement, nous allons donc créer des groupes de déploiements. Chaque groupe définit un ensemble de service-units (fournisseurs et consommateurs de service) qui seront déployés sur un même noeud. Ces noeuds seront ensuite répliqués. Prenons ici un exemple de groupe : les fournisseurs et les consommateurs. Deux groupes, avec réplication, cela veut dire qu’il nous faut au moins 4 noeuds.

– Noeud 1 : tous les fournisseurs (SU provides, avec end-point auto-généré).
– Noeud 2 : tous les fournisseurs (SU provides, avec end-point auto-généré). Réplication du noeud 1. On déploie les même SU grâce aux end-points générés au déploiement.
– Noeud 3 : tous les consommateurs (SU consumes, invocation par nom d’interface et nom de service seulement).
– Noeud 4 : tous les consommateurs (SU consumes, invocation par nom d’interface et nom de service seulement). Réplication du noeud 4. On suppose que les consommateurs peuvent écouter les événements externes de manière concurrente. Je reviendrai sur ce point en dernière partie de cet article.

Schématiquemement, cela nous donne donc…

4 noeuds, avec spécialisation et réplication

Il n’y a qu’un seul point d’entrée dans le bus, c’est l’un des 2 consommateurs. Celui-ci va appeler un fournisseur, qui peut être sur le noeud 1 ou sur le noeud 2 (ça, c’est le routage applicatif). Ce fournisseur relaiera la requête à la ressource externe avant de faire remonter la réponse. Si un noeud fournisseur tombe, il en reste un deuxième. Petals enverra automatiquement les requêtes vers le noeud toujours vivant. Si un noeud consommateur tombe, c’est soit le load balancer en amont, soit directement le composant, qui s’assurera que le 2ème consommateur prend le relais.

Solution 2 : la réplication des noeuds

La deuxième solution est la plus simple. Il s’agît de répliquer tous les noeuds Petals. Ici, il n’y a pas de spécialisation des noeuds. Si l’on prend notre exemple précédent, on déploierait donc les fournisseurs et les consommateurs sur chaque noeud. Afin de gagner en performances et d’éviter un transport inutile, il faudrait forcer chaque consommateur à appeler le fournisseur présent sur le même noeud que lui. Cela tombe bien, c’est d’ailleurs la configuration par défaut de Petals.

Pour rappel, cela se configure dans le routeur et par noeud. Cela permet de pondérer les choix de Petals sur le routage applicatif. Du coup, on peut obtenir le même résultat que précédemment avec seulement 2 noeuds.

2 noeuds répliqués

Si un noeud tombe, le deuxième prendra le relais.

Et si on veut mettre 3 noeuds, il suffit de changer la topologie (topology.xml) et éventuellement de mettre à jour le load balancer en frontal. Si vous voulez aller à n noeuds, ça passe aussi.

N noeuds répliqués

Après, quant au choix du nombre de noeuds, il faut aussi prendre en compte la synchronisation des noeuds (périodicité, y’aura-t-il des déploiements et désinstallations souvent) et peut-être mettre en place une surveillance particulière du noeud maître. Mais ça ne change pas le principe sous-jacent.

Il existe aussi une variante à cette configuration, c’est plutôt que d’avoir 1 topologie de n noeuds, on peut préférer n topologies de 1 noeud. Sur cet exemple en tout cas, ce serait rigoureusement identique. On peut aussi mêler les 2 approches et répliquer des topologies. C’est assez flexible. Après, il faut voir combien de processus (chaînes de traitement) cohabitent.

Conditions sine qua non de la HA

On l’a vu ici, il y a de nombreuses façons d’organiser et répartir des noeuds Petals pour assurer sa haute disponibilité. Cependant, la HA ne peut concerner uniquement Petals. Elle doit aussi concerner les ressources externes accédées par le bus. Ces ressources, elles sont à l’entrée et à la sortie du bus, c’est-à-dire du côté des applications fournisseurs et des applications consommateurs.

En clair, l’application qui fournit une fonctionnalité (et accessible par l’application cliente au travers du bus) doit elle-aussi être hautement disponible. Cela peut passer par un autre load balancer, ou bien par des disques partagés, ou bien par un autre moyen qui garantit la disponibilité de la ressource ou de l’application.

HA de l'application fournisseur

Un deuxième élément porte sur la haute disponibilité du côté des consommateurs de services dans Petals. Les consommateurs, ce sont les service-units qui vont propager un événement externe au bus (comme une requête ou un fichier qui apparaît dans un dossier) sous la forme d’un message adressé à un forunisseur de service.

HA des consommateurs dans Petals

J’ai évoqué ce point au milieu de cet article.

On suppose que les consommateurs peuvent écouter les événements externes de manière concurrente.

Pour ce dernier point, voilà 2 exemples pour vous aider à comprendre :

  • Un consommateur associé au connecteur SOAP est en attente de requêtes SOAP. En cas de réplication d’un tel consommateur, la HA est garantie en plaçant un load balancer en amont. Ce load balancer sera chargé d’envoyer la requête sur un des consommateurs répliqués.
  • Un consommateur associé au connecteur File (Transfer) surveille un répertoire sur un disque. Il guette l’apparition de nouveaux fichiers. En cas de réplication d’un tel consommateur, la HA est garantie par le composant, qui supporte des accès concurrents au dossier surveillé.

La HA des consommateurs permet de garantir la pérennité du système, même si l’un des consommateurs venait à tomber.

Au final, c’est donc la combinaison de 3 choses qui permet assure la haute disponibilité de l’infrastructure :

  • La HA du bus (transport des messages).
  • La HA des ressources / applications accédées par le bus.
  • La détection concurrente et unique d’événements externes à propager dans le bus (pour les consommateurs).

Pour terminer…

Je vais rajouter une précision qui me semble évidente, mais sait-on jamais. La haute disponibilité de l’infrastructure ne garantit pas le bon traitement des messages ou des événements. Les processus doivent être construits de sorte à gérer les cas d’erreur. Petals fournit les mécanismes de base (au travers des MEP, des politiques de réémission de messages et des timeouts), mais ceux-ci doivent aussi être exploités et pris en compte lors de la conception des services et de leurs consommateurs à déployer dans le bus. C’est évidemment un tout autre sujet.

Cert article vous a présenté la haute disponibilité d’une infrastructure utilisant Petals ESB. Dans un prochain article, je présenterai très rapidement un cas plus concret afin d’illustrer la configuration d’un load balancer logiciel (Apache + mod proxy balancer).

Publicités

Petals ESB n’est pas un load balancer !

A la base, cet article devrait expliquer l’utilisation de Petals ESB avec un load balancer en frontal du bus.
Le load balancer en question étant un serveur Apache avec le module « mod proxy balancer ». Mais finalement, il m’a semblé préférable de faire un billet intermédiaire pour expliquer ce qui n’est pas toujours évident à priori : Petals n’est pas un load balancer.

Petals ESB est composé de 3 principaux niveaux.

  • D’abord, il y  a le conteneur, que l’on appelle parfois abusivement le kernel. C’est le socle de l’infrastructure, qui assure le transport des messages, contient l’annuaire de services, gère le routage, conserve l’état des services, etc.
  • Ensuite, il y a les composants. Les composants se déploient sur le conteneur (cette relation conteneur – composants est définie dans la spécification JBI et est équivalente à ce qu’on peut trouver avec les EJB, OSGi ou SCA). Les composants fournissent une base de code réutilisable et configurable. Ils permettent d’implémenter la logique fonctionnelle.
  • Enfin, dernier niveau, ce sont les (fournisseurs de) services et les consommateurs de services. Ceux-ci sont créés sous la forme de configurations des composants. Cela permet d’implémenter des unités logiques et de les assembler pour répondre à des besoins fonctionnels.

Un service dans Petals est identifié par 3 composantes.

  • En premier lieu, il y a ce qu’on appelle le nom d’interface. Cela correspond à un contrat technique : à minima, on y trouve une liste d’opérations et les paramètres associés. Actuellement, un tel contrat se présente sous la forme d’un WSDL. Mais au-delà des aspects techniques, un contrat correspond aussi et surtout à une fonctionnalité, que l’on va pouvoir utiliser et invoquer dans son système d’information.
  • Vient ensuite le nom de service. Un service implémente une interface donnée. Dans la pratique, cette partie permet de distinguer des implémentations différentes. Par exemple, on pourrait avoir un contrat implémenté avec du Java (serviceJava) et avoir une autre implémentation en BPEL (serviceBpel). La fonctionnalité rendue serait la même, mais les propriétés seraient différentes (ici, il y en a un qui s’exécutera plus vite que l’autre). On pourrait aussi imaginer un cas plus métier. On peut imaginer avoir un contrat de réservation de voiture, et avoir une implémentation par société de location de voitures. Ce qui différencie alors les services, ce n’est pas la technologie d’implémentation mais le fournisseur.
  • Enfin, il y a le nom d’end-point. Cela correspond à un point de déploiement.

Un service déployé dans Petals possède toujours ces 3 parties.
La relation de cardinalité est la suivante :

interface 1 – * service(s) 1 – * end-point(s)

Autrement dit, à partir de l’annuaire de services du bus, on peut construire une hiérarchie.

Si on part de ce schéma, on aurait 6 services dans l’annuaire, identifiés par 6 triplets.

  1. Reservation Contrat / Res Service 1 / edpt 1
  2. Reservation Contrat / Res Service 1 / edpt 2
  3. Reservation Contrat / Res Service 2 / edpt 1
  4. Reservation Contrat / Res Service 3 / edpt 5
  5. Reservation Contrat / Res Service 3 / edpt 3
  6. Reservation Contrat / Res Service 3 / edpt 4

Dans Petals, du point de vue d’un consommateur, on peut utiliser cet arbre pour se donner de la flexibilité lors de l’invocation.

  • Cas explicite : le consommateur donne le triplet complet. On appellera donc ce service, qui implémente ce contrat, et qui est déployé au niveau de ce end-point.
  • Cas implicite : on invoque par nom d’interface et nom de service ou juste par nom d’interface. Dans ce cas, on va naviguer dans notre arbre et nous arrêter à un niveau intermédiaire plutôt qu’à une feuille. Le chemin restant (pour sélectionner un service concret) sera choisi par le bus. Par exemple, si l’on invoque juste par nom d’interface sur notre exemple, n’importe lequel des 6 services peut être choisi par le bus.

Pour une même interface, lorsque je trouve plusieurs services correspondants, j’ai une équivalence de services. Ils rendent la même fonctionnalité. Mais il peut y avoir des propriétés différentes : soit sur les performances, soit sur le domaine métier impliqué.

Pour un même service, lorsque je trouve plusieurs end-points correspondants, j’ai une réplication de services. C’est la même implémentation, le même service, la même interface, mais ce sont des points de déploiement différents, que l’on peut utiliser pour de la haute disponibilité.

Dans le cas d’une invocation implicite, c’est donc le bus qui sélectionne les informations manquantes (nom de service, nom d’end-point). Ce choix est actuellement basé sur des critères de proximité. Petals étant distribué, on va pouvoir privilégier des services proches du consommateur ou au contraire situés sur d’autres noeuds. Si après l’application de ce premier critère, il reste de multiples candidats, alors le bus choisira de manière aléatoire le service à invoquer. On pourrait bien sûr imaginer d’autres stratégies finales, par exemple basées sur la consommation des ressources, pour choisir le service susceptible de répondre le plus vite. Mais cela n’est pas implémenté pour le moment et il n’est pas prévu que ce soit fait à court terme.

Au final, cet article visait à souligner une chose.
Petals ESB sait faire du routage fonctionnel. Mais, ce n’est pas un load balancer. La nuance est très importante lorsque l’on conçoit une architecture avec Petals. Et autant, on pourrait enrichir le module de routage d’une stratégie avec équilibrage de charges (pour ce qui transite à l’intérieur du bus), autant cela ne suffirait pas pour les interactions avec des applications externes.