La notion de composants avec ou sans état est très importante pour le développement d'applications réparties et de
systèmes, même si elle n'a intégré le vocabulaire courant que récemment. Cette notion signifie que si deux composants
ou deux services sont en train de communiquer et qu'un état est géré pour la durée de la conversation avec le client,
il est possible qu'un incident survienne dans le composant serveur (ou qu'il y ait une défaillance du réseau)
signifiant que le client ne peut pas terminer la conversation et doit recommencer. Cette notion implique également que
le réacheminement des requêtes client vers un des ensembles de composants est effectué, à moins que l'ensemble des
composants partage le même magasin de données pour l'état de la conversation. Ce problème est très courant dans le
développement d'applications Web et l'état de l'application est soigneusement géré de façon à l'éviter autant que
possible. L'état de l'application est géré par le client, par la conversation elle-même (en passant l'état dans chaque
message) ou par des composants avec état côté serveur conçus de manière précise. L'exemple le plus courant pour les
interactions Web avec état est le panier. Les utilisateurs s'attendent à ce que leur panier soit conservé s'ils
quittent leur poste quelques instants, mais comment cela est-il possible avec 100 000 utilisateurs connectés
simultanément ?
Cependant, cela ne signifie pas que les composants avec état sont préjudiciables par nature ; ils sont simplement une
source potentielle d'incidents au niveau des performances et de la résilience, à moins d'être gérés et développés à
l'aide de standards plus rigoureux. En fait, toutes les applications de gestion contiennent des services dont la nature
est de gérer ou de représenter les entités avec état ou qui contiennent des services auxquels il faut accéder dans un
certain ordre. En fait, l'architecture J2EE définit une bean session avec état et une bean session sans état pour
souligner ces questions de manière explicite ; elle définit également certaines restrictions pour les composants avec
état. Cela donne une classification simple des services avec état, notamment des raisons faisant qu'on ne peut pas les
éviter. Il peut être obligatoire pour un service d'avoir un état pour l'une des raisons suivantes :
Le service conserve l'état du client : c'est l'exemple du panier. Pour une raison ou pour une autre, des
données doivent être conservées sur des appels entre le client et le service. Ces données font partie de la
conversation et, si on n'y prête pas attention, peuvent donc associer le client à des ressources serveur
spécifiques.
Le service gère une ressource avec état : dans ce cas, le service gère souvent un ensemble de ressources ou
d'entités, chacune d'entre elles ayant un état. Par exemple, une commande client a un état, un commutateur réseau a un
état, etc. L'interface d'un service qui gère l'objet en fermant ou en différant une commande, ou encore en redémarrant
un commutateur, change donc l'état d'une entité en particulier.
Le service a un protocole avec état : dans ce cas, le service effectue les opérations qu'il fournit dans un
ordre logique. Il comporte, par exemple, les opération login (), dostuff () et logoff () ; ce dont on peut déduire
qu'il n'est pas possible d'appeler les opérations dostuff () et logoff () si l'opération login () n'a pas été
appelée.
Une autre forme d'état très courante dans les architectures de composants, mais qui n'est pas applicable dans le monde
des services, est la notion d'état transactionnel. Dans le contexte des composants, il est possible d'établir qu'une
méthode get () et update () sur un composant soit appelée par un client dans le cadre d'une transaction créée et
maintenue par le client. La méthode update() doit changer un stockage transactionnel sous-jacent. Cela implique
l'intervention de la plateforme middleware pour coordonner les transactions et s'assurer que les méthodes nécessitant
des transactions soient appelées par un client ayant une transaction ouverte. Pour les services, il n'est pas conseillé
de suivre un modèle dans lequel les transactions, au sens classique de validation en deux phases, restent ouvertes pour
plusieurs appels de services. Des standards sont en cours de développement pour les transactions des différents appels
de services, mais ils suivent un paradigme fondamentalement différent (une compensation) et sont pris en charge
différemment par les plateformes middleware.
La technique la plus évidente pour le développement efficace des services avec état, que nous avons déjà évoquée,
consiste à externaliser l'état du service, rendant ainsi explicite le fait que ce service a un état, mais aussi que cet
état peut être identifié comme faisant partie de la spécification de service. Cette question est abordée ci-après pour
les deux classes de services avec état.
Comme la plupart des services logiciels seront développés à partir d'une plateforme middleware existante, comme J2EE ou
Microsoft.NET, ces architectures de plateforme comportent des techniques d"implémentation pour faciliter la gestion de
l'état. Ces instructions portent donc principalement sur des techniques de conception pour certaines classes de
services avec état. Il convient aussi de remarquer qu'il ne s'agit pas d'une préoccupation nouvelle. Dans le cadre du
développement de grands systèmes, les développeurs, les concepteurs et les architectes ont rencontré et décrit depuis
des années le développement de transactions interactives et non interactives à l'aide d'IBM Customer Information
Control System avec des clients écran vert (actuellement 3270 terminaux).
Il vaut mieux éviter cette situation. Quand cela est possible, si une conception fait appel à la gestion d'état lors de
la conversation entre un service et ses clients, il vaut mieux décider de choisir une autre approche. Si cela n'est pas
possible, externalisez cet état en faisant passer toutes les données d'état nécessaires entre le service et le client
avec chaque message indiquant l'intégralité de la conversation. Cette approche peut signifier que la taille des
messages a beaucoup augmenté, mais le service lui-même est à présent totalement sans état. Une autre approche consiste
à placer un identificateur de conversation dans chaque message et à conserver l'intégralité de la conversation dans un
magasin de stockage de données, comme une base de données. Si cela entraîne des conséquences importantes sur les
performances du serveur, elles peuvent être contrebalancées par les performances du réseau et du client entraînées par
des messages plus petits.
Ces services sans état ont pour objectif premier de fournir un ensemble de services identiques pouvant gérer n'importe
quelle requête en utilisant des techniques d'équilibrage de charge pour distribuer les clients. Cet équilibrage de
charge est possible si tout l'état est externalisé et conservé dans le même magasin de stockage de données.
Dans ce cas, il faut considérer la gestion des ressources qui ont un état explicite. En fait, cet état est un aspect
important de la ressource elle-même. Il est possible que l'on puisse décrire l'état de la ressource, la commande du
client ou le commutateur réseau décrits ci-dessus en utilisant une machine d'état, en décrivant les états valides, mais
aussi la façon dont les opérations fournies par le service affectent l'état des ressources sous-jacentes.
Lors de cette description, il faut remarquer que cet état fait partie intégrante des ressources. Toutefois, cela n'est
pas forcément exprimé de manière explicite dans le modèle d'informations qui la représente. Il faut aussi souligner que
lorsque l'on gère un ensemble d'entités, il faut être capable d'identifier les ressources individuelles que l'on
affecte, qu'elles aient ou non un identificateur explicite.
Lorsqu'un service représente l'accès à une entité physique ou la requête de l'état d'une entité physique, comme un
commutateur réseau ou un élément de contrôle de processus, il n'est pas possible d'externaliser l'état de l'entité.
L'analyse d'une valve est le seul moyen de connaître son état. Même s'il est possible de construire et de répondre à
l'aide d'un message décrivant l'état actuel de la valve, cela n'est pas une situation permanente. L'état de la valve
peut changer au cours de la transmission ou du traitement de ce message.
Dans le domaine des services Web, il existe un ensemble de nouveaux standards, le WSRF (Web Services Resource
Framework) qui porte sur les patterns des services avec état et sur les approches pour coder l'état, principalement
pour les services représentant la gestion de ressources avec état. Pour plus d'informations, consultez le site IBM WS-ResourceFramework.
L'exemple mentionné ci-dessus implique un service ayant une organisation logique des opérations qu'il fournit. De
nombreux services fourniront des interfaces de cette forme. Cela est parfois lié aux ressources d'état, mais alors
l'ordre des opérations dépend de l'état de la ressource gérée. Dans ce cas, l'ordre dépend de la conversation
elle-même. L'exemple suivant illustre la spécification d'un service ayant un protocole associé, en commençant par la
spécification structurelle, puis une machine d'état indiquant la spécification du comportement est décrite.
La commande peut avoir l'un des états suivants {Ouverte, Annulée, Réalisée, Fermée) et change en fonction des
opérations fournies par la spécification ci-dessus. Dans le cas d'une autotransition de l'état ouvert, l'opération
ChangementCommande est réalisée par l'envoi de la notification de ce changement.
Lorsque les services sont développés au sein d'une entreprise unique et avec une portée technique, des spécifications
de comportement détaillées peuvent être développées ou décrites de manière moins formelle, sous forme de texte. Lorsque
les services sont exposés en dehors de ce type de portée, par exemple entre des partitions, ils représentent une
spécification logique des interactions entre les partitions et doivent être développés plus en détails. Les
spécifications détaillées permettent aussi une réutilisation plus efficace par les utilisateurs lorsque les services
sont censés être réutilisés souvent.
|