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).

Petals en distribué sur des Machines Virtuelles

Il se trouve qu’au début de l’été, j’ai eu à expérimenter certaines choses avec Petals.
Rien de révolutionnaire, mais il s’agissait de choses que je n’avais jamais tentées.

L’une d’elles concernaient l’utilisation de Petals avec des machines virtuelles. Je savais que cela était possible, plusieurs personnes l’ayant déjà fait par le passé. Cela ne m’a d’ailleurs pas pris très longtemps, mais je préfère me laisser des notes au cas où j’aurais à le refaire plus tard. Ce blog fera très bien l’affaire pour cette tâche. Et si jamais vous voulez valider le fonctionnement de Petals sur des VM, ce post pourra vous aider.

 

Installation des VM

D’abord, j’ai testé le tout sur Ubuntu (10.04 LTS).
J’ai installé VirtualBox pour émuler un deuxième hôte sur ma machine (ce deuxième hôte était une autre version d’Ubuntu dont j’avais récupérée l’image sur le net). Pour ces étapes, le mieux est de s’inspirer de la documentation assez riche sur le sujet.

http://doc.ubuntu-fr.org/virtualbox
http://www.siteduzero.com/tutoriel-3-36484-virtualisez-un-systeme-d-exploitation-avec-virtualbox.html#ss_part_2
http://videonoob.fr/tutoriel/virtualbox-installer-ubuntu
http://www.virtualbox.org/manual/ch06.html

En gros, cette étape consiste à installer dans une machine virtuelle un système d’exploitation donné.
Le principe du test, celui qui permet de vérifier le fonctionnement de Petals sur des VM, est d’avoir une topologie distribuée sur 2 noeuds : un noeud sur l’hôte courant et l’autre sur une VM (on peut aussi s’amuser à créer plusieurs VM). Dans la mesure où l’on fait du distribué, il faut que chaque VM puisse communiquer avec l’hôte courant et les autres VM. Pour cela, il faut penser à activer la bonne configuration réseau dans VirtualBox. J’avais pour ma part utilisé le mode Host-only networking de VirtualBox.

 

Installation de Petals en distribué

Une fois qu’on a nos VM, il faut maintenant installer Petals et le paramétrer pour fonctionner en distribué.
L’installation consiste à dézipper l’archive de Petals sur chaque VM. Puis on met à jour le fichier topology.xml.

<?xml version="1.0" encoding="UTF-8"?>
<tns:topology xmlns:tns="http://petals.ow2.org/topology"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://petals.ow2.org/topology petalsTopology.xsd">
	<tns:domain mode="static" name="PEtALS">
		<tns:description>The static domain configuration</tns:description>

		<!--
			defaut configuration to use MySql as centralize petals registry
			<tns:jndi>
			<tns:factory>org.ow2.petals.registry.database.RegistryInitialContextFactory</tns:factory>
			<tns:provider-url>jdbc:mysql://localhost:3306/mysql</tns:provider-url>
			<tns:security-principal>root</tns:security-principal>
			<tns:security-credentials>mysql</tns:security-credentials>
			<tns:pool-size>5</tns:pool-size> <tns:batch-size>10</tns:batch-size>
			</tns:jndi>
		-->

		<tns:sub-domain name="subdomain1" mode="master-slave">
			<tns:description>description of the subdomain</tns:description>
			<tns:container name="0" type="master">
				<tns:description>description of the container 0</tns:description>
				<tns:host>192.168.38.100</tns:host>
				<tns:user>petals</tns:user>
				<tns:password>petals</tns:password>
				<tns:webservice-service>
					<tns:port>7600</tns:port>
					<tns:prefix>petals/ws</tns:prefix>
				</tns:webservice-service>
				<tns:jmx-service>
					<tns:rmi-port>7700</tns:rmi-port>
				</tns:jmx-service>
				<tns:transport-service>
					<tns:tcp-port>7800</tns:tcp-port>
				</tns:transport-service>
				<tns:registry-service>
					<tns:port>7900</tns:port>
				</tns:registry-service>
			</tns:container>

			<tns:container name="1" type="slave">
				<tns:description>description of the container 1</tns:description>
				<tns:host>192.168.38.106</tns:host>
				<tns:user>petals</tns:user>
				<tns:password>petals</tns:password>
				<tns:webservice-service>
					<tns:port>7601</tns:port>
					<tns:prefix>petals/ws</tns:prefix>
				</tns:webservice-service>
				<tns:jmx-service>
					<tns:rmi-port>7701</tns:rmi-port>
				</tns:jmx-service>
				<tns:transport-service>
					<tns:tcp-port>7801</tns:tcp-port>
				</tns:transport-service>
				<tns:registry-service>
					<tns:port>7901</tns:port>
				</tns:registry-service>
			</tns:container>

			<!--
			<tns:container name="2" type="slave">
				<tns:description>description of the container 2</tns:description>
				<tns:host>localhost</tns:host>
				<tns:user>petals</tns:user>
				<tns:password>petals</tns:password>
				<tns:webservice-service>
					<tns:port>7602</tns:port>
					<tns:prefix>petals/ws</tns:prefix>
				</tns:webservice-service>
				<tns:jmx-service>
					<tns:rmi-port>7702</tns:rmi-port>
				</tns:jmx-service>
				<tns:transport-service>
					<tns:tcp-port>7802</tns:tcp-port>
				</tns:transport-service>
				<tns:registry-service>
					<tns:port>7902</tns:port>
				</tns:registry-service>
			</tns:container>
			-->
		</tns:sub-domain>
	</tns:domain>
</tns:topology>

Ici, j’ai une topologie de 2 noeuds, avec une relation maitre / esclave. On peut évidemment rajouter d’autres noeuds, qui seront configurés en esclave. Il n’y a qu’un maître par topologie, et celui-ci sert de référent aux autres noeuds pour synchroniser leur annuaire de services. Cela permet à chaque noeud d’être indépendant tout en étant capable de communiquer avec les autres. Un des inconvénients toutefois, c’est que le noeud maître revêt une importance plus grande que les autres. On peut cependant s’assurer de sa haute disponibilité avec un outil tel que Heartbeat. Et cela dépend aussi de la fréquence des déploiements sur la topologie. La synchronisation entre les noeuds n’est nécessaire que lors de l’installation ou la désinstallation de services sur le bus.

Une fois que l’on a défini sa topologie, on peut soit la copier dans chaque installation de Petals (dossier conf), soit la mettre sur un serveur web et faire pointer chaque noeud sur l’adresse du fichier. Cette deuxième méthode est la plus pratique en mode distribué. L’adresse est définie dans le fichier conf/server.properties de chaque noeud.

# Alternate topology configuration file URL. This value must be a valid URL like :
#  - http://localhost:8080/petals/topology.xml
#  - file:///home/petals/config/topology.xml
#  - or any valid URL (java.net.URL validation)
# If not specified, the local topology.xml file is used
petals.topology.url=http://192.168.38.106:8080/petals/topology.xml

 

Déploiement de services

Résumons…
Nous avons installé nos serveurs virtuels.
Nous avons installé Petals et l’avons configuré en distribué.

On va maintenant faire un petit test mettant en oeuvre le routage interne de Petals. Pour cela, on va déployer le composant Clock de Petals. Ce composant, véritable concentré de technologie, fournit un service qui donne l’heure. On le déploie donc sur chaque instance de Petals (chaque noeud).

Du point de vue de l’annuaire de services, on a la hiérarchie suivante :

  • Interface : Clock
  • Service : ClockService
  • End-points : edpt-1, edpt-2, …, edpt-n, où chaque edpt pointe vers un noeud Petals.

A chaque fois que l’on déploie notre composant, le même service est enregistré dans Petals. Ce service est identifié par un nom d’interface (un contrat technique, cad une liste d’opérations, chaque opération ayant des paramètres d’entrée et/ou de sortie), un nom de service (qui identifie une implémentation) et un nom d’end-point (qui identifie un point de déploiement). Comme c’est le même service qui est déployé à chaque fois, le nom d’interface et de service est le même pour chaque enregistrement. Seul change le nom d’end-point, qui est généré au déploiement (c’est un comportement par défaut de ce composant Clock).

On a donc un service à invoquer, service qui est répliqué sur chaque noeud du bus.
Il reste à l’invoquer. Pour cela, on utilise le sample client, un autre composant de Petals, qui est à la fois rustique et pratique. On le déploie une seule fois, sur l’hôte courant. Une fenêtre Swing s’ouvre (c’est le comportement du composant, lorsqu’il est démarré, il montre une petite console graphique).

Dans l’onglet Query, cliquer sur Get all the endpoints.
Tous les services du bus sont alors listés. On peut alors en sélectionner un. En mettant à jour les propriétés d’invocation, et plus exactement en n’invoquant que par nom d’interface et nom de service, on peut alors demander au bus de choisir un service qui vérifie ces critères parmi ceux disponibles. Le service finalement choisi par le bus (selon les critères fournis par le consommateur) peut être situé sur le même noeud que le consommateur ou bien être sur un autre noeud de la topologie. En fait, tout cela est transparent pour le consommateur de service (ici, le sample client).

En réitérant l’appel, on peut ainsi vérifier les capacités en distribué de Petals.
Exemple : en invoquant un service ClockService qui implémente le contrat Clock

  • Appel 1 : on tombe sur edpt-2
  • Appel 2 : on tombe sur edpt-1
  • Appel 3 : on tombe sur edpt-1 (encore).
  • etc…

A noter !

Suite à un commentaire, je me dois de préciser que le fichier server.properties du noeud sur lequel a été déployé le sample client a été modifié pour pouvoir appeler de manière aléatoire n’importe quel service de toute la topologie. Il suffit pour cela de mettre

petals.router.strategy=random,2,2,1

La configuration du routeur de Petals sera bientôt documentée sur le wiki de Petals…

Donc ici, comme il y a plusieurs services « ClockService implémentant le contrat Clock », et que l’invocation ne spécifie pas le end-point à appeler, alors c’est le bus qui le choisit pour nous. En cas d’équivalence de service, le choix est effectué de manière aléatoire. D’autres stratégies pourraient être utilisées, comme un round-robin, la qualité de service ou basé sur la consommation des ressources. Cependant, ces autres stratégies ne sont pour le moment pas implémentées.

On peut aussi suivre et visualiser les appels avec la web console de Petals.
Je ne l’ai pas fait ici, mais pour les curieux, il y a un guide de démarrage pour Petals qui contient entre autres des exercices avec la web console.

 

Au sujet de la synchronisation…

J’ai dit plus haut qu’en distribué, le noeud maître servait de référent pour synchroniser les annuaires de services. Cette synchronisation est périodique et configurée dans le fichier server.properties de chaque noeud esclave. Lorsque l’on déploie un service, il faut donc attendre que la période se soit écoulée pour que le service déployé soit visible depuis les autres noeuds.

Cependant, il est possible de forcer une synchronisation, soit avec JMX, soit en mode console (option de démarrage avec Petals 3.x, et possibilité d’utiliser une console externe, que l’on appelle Petals-CLI, et qui sera promue avec la version 4 de Petals ESB).