• Aucun résultat trouvé

3.2.1 Mémoire partagée

Les systèmes à mémoire partagée distribuée peuvent être considérés comme des implémentations sophistiquées de systèmes distribués. Le but de ces systèmes est très différent des intergiciels distribués usuels. En effet, ils ne sont utilisés qu'à des buts d'Informatique Parallèle où les demandes de ressources de calcul peuvent être importantes. Ainsi, la recherche dans ces systèmes tente de fournir des sémantiques correctes et efficaces pour l'exécution de programmes multi-threadés [ACDK96]. Habituellement, ces systèmes proposent un langage de programmation spécifique où l'utilisateur indique les propriétés distribuées des données partagées. La recherche de performance repose sur l'élaboration de modèles complexes de cohérence mémoire. La cohérence des structures partagées sur plusieurs stations de calculs a un coût et peut devenir un goulot d'étranglement pour les applications distribuées. Les systèmes DSM sont couteux par la mémoire et les capacités des processeurs demandées par la réplication synchronisée des données entre machines.

La combinaison d'une machine virtuelle et d'un tel système [YuC97] montre un système où la distribution est rendue transparente sur des plateformes hétérogènes – grâce à l'homogénéité procurée par la technologie commune de machine virtuelle. La transparence de programmation de Java/DSM est exceptionnelle tout en restant un système coûteux pour les applications réparties de l'Informatique Pervasive.

3.2.2 Passage par copie, passage par copie et restauration

Les intergiciels distribués des systèmes de communication d'entreprises (B2B) et de contrôle d'équipements reposent sur des modèles aux conséquences moins lourdes que celles de la mémoire partagée. La mémoire partagée impose un protocole sophistiqué de maintenance de cohérence entre espace d'adressages distincts. En dehors des cas d'utilisation de l'Informatique Parallèle, souvent pertinents pour des opérations de calculs importantes, cette maintenance de cohérence permanente entre objets partagés est peu pertinente.

Les appels distants dans les intergiciels basés sur un langage ne tentent pas de garder la même sémantique que les appels locaux sur des variables accédées au travers de pointeurs mémoire (références). La puissance des langages de programmation est généralement utilisée pour la construction de structures complexes qui sont basées sur le partage de références dans un tas (heap). Dans les situations distribuées, l'espace d'adresse étant distinct, les appels distants ne pourront pas bénéficier des avantages importants des langages de programmation. La sémantique usuelle de l'appel distant est le "passage par copie" (call-by-copy) – aussi appelé "passage par valeur". Un paramètre passé dans un appel distant est l'objet d'une copie en profondeur. Aucune modification à distance sur l'objet passé n'est répétée sur l'objet local.

Une alternative au passage par copie est le "passage par copie et restauration". Cela indique qu'après l'appel de méthodes, toutes les modifications effectuées par l'appelé sur les données passées sont transférées à l'appelant et répercutées sur ses données. La "restauration" implique des complications posées par la délicate gestion de structures de données dans le tas.

Le "passage par copie et restauration" diffère du "passage par référence" si les données sont utilisées par l'appelé après l'appel (serveur à état) et si l'appelant exécute un programme multi-threadé où les données sont sujettes à une modification parallèle durant l'appel.

Le passage par référence n'est possible que dans certains langages de programmation comme Java, il est toutefois restreint à certains cas d'utilisation. Avec Java RMI, les références distantes passées en arguments d'appel de méthodes distantes doivent implémenter l'interface UnicastRemoteObject. Le mécanisme est couteux puisque tout appel sur cette référence distante doit alors être retransmis au système appelant.

NRMI (Natural Remote Method Invocation) [TiS03] est une version modifiée de Java RMI. NRMI met en œuvre un mode appelé "passage par copie et restauration" (call-by-copy-restore) à la place du simple mode "passage par copie" (call-by-copy) de RMI pour les arguments n'implémentant pas l'interface UnicastRemoteObject. Les auteurs affirment que NRMI garantit que les appels de méthodes distantes seront identiques aux appels de méthodes locaux dans le cas où les clients utilisent un processus unique et les serveurs n'ont pas d'états, c'est-à-dire qu'aucun état accessible par un argument des méthodes n'est maintenu par le serveur.

3.2.3 Patron de conception "Remote Proxy"

Le patron de conception "Remote Proxy" ou "mandataire distant" est l'évolution des techniques RPC (Remote Procedure Call) dans la programmation orientée objet. RPC est un paradigme répandu qui permet d'implémenter le modèle client-serveur en Informatique Distribuée. Le client initie la connexion RPC en envoyant une requête à un serveur distant connu afin d'exécuter une procédure avec les paramètres donnés. Une réponse est envoyée au client où l'application reprend son cours. Tant que le serveur traite la requête, le client est bloqué.

Au-delà de l'implémentation du modèle architectural client-serveur, les techniques RPC permettent, dans une certaine mesure, de rendre transparent l'aspect distribué de l'appel de procédure. Idéalement, le développeur utilise une interface de procédure identique pour les appels locaux comme pour les appels distants. C'est l'implémentation de la procédure qui appelle directement une entité locale ou relaie l'appel à un serveur distant. Le retour de la procédure retourne dans un cas le résultat de l'appel de l'entité locale, dans l'autre cas le résultat de l'appel distant reformé dans le format indiqué par l'interface. L'implémentation locale de l'interface de procédure qui relaie la requête dans un protocole distribué est appelé "stub" (ou "talon") tandis que l'implémentation distante qui interprète les requêtes dans le protocole distribué pour reformer l'appel sur le langage de programmation coté serveur est appelé "skeleton" (ou "squelette").

Le patron de conception "Remote Proxy" répond aux situations d'appels de méthodes sur des objets distants. Un patron de conception est une réponse à un problème récurrent :

Le problème est que l'appel de méthodes se fait entre objets définis dans des espaces d'adresses différents et que la communication répartie nécessite habituellement la programmation de transformation d'appels dans un protocole particulier.

La solution au niveau langage rend identique l'appel local et l'appel distant de ces objets dans la phase de développement. Ce patron de conception reprend le patron nommé "Proxy" [GHJV95] dans le cadre d'applications distribuées. De même qu'avec RPC, stubs et skeletons sont utilisées de part et d'autre de la connexion distribuée entre un client et un serveur distant. La différence est que le stub est encapsulé dans un objet implémentant la même interface objet de l'objet distant.

Figure 4 Appel de méthode distante [KrS05]

Les détails d'un appel distant à partir du client local au travers du stub sont illustrés par la Figure 4. Lorsque le client invoque la méthode sur l'objet proxy, le stub "sérialise" les paramètres (marshalling), construit la requête dans le protocole distribué et envoie la requête à l'adresse indiquée par la référence de l'objet distant, référence construite ou obtenue au préalable. Coté serveur, le squelette effectue les mêmes opérations dans le sens inverse : dé-sérialise les paramètres (unmarshalling), relaie l'appel à la méthode de l'objet indiqué dans la requête, sérialise les paramètres de retour et les envoie dans le protocole distribué au stub qui dé-sérialise les paramètres pour les retourner dans la réponse au client. Le code du client séparé du stub est semblable à celui d'un client qui s'adresserait à un objet local.

3.2.4 Patron de conception "export-bind"

Le patron de conception nommé "export-bind" [KrS05] dérive des concepts de RM-ODP [RM-ODP95] où sont définies les liaisons – "bindings" – et les références des objets à lier –

"references". "export-bind" répond apporte une solution à un problème récurrent en Informatique Distribuée :

Problème : le client et le serveur d'une interface programmatique ne sont pas conçus pour fonctionner de manière distante avant l'exécution. Le client connaît toutefois le nom de l'objet à appeler.

Solution : la solution repose sur les notions de serveur de nom – Name Server – d'usine à exporter – Export Factory – et d'usine à liaisons – Binding Factory (cf. Figure 5). Le serveur de noms est un annuaire de références nommées. L'espace de nommage et le serveur de nom sont partagés par le client et le serveur. Le serveur, dès qu'il est prêt à servir son interface de manière distribuée, appelle l'usine à exporter afin que celle-ci construise la référence ((a) step 1) nommée et l'enregistre auprès du serveur de noms ((a) step 2). Le client recherche le serveur par son nom auprès du serveur de nom ((b) step 1). Le serveur de nom lui retourne la référence associée. Cette référence est conçue pour être interprétable par l'usine à liaison. L'usine à liaison est un composant accessible dans l'environnement d'exécution du client. Le client passe la référence à l'usine à liaison qui crée ou obtient un objet proxy encapsulant le stub de l'objet distant ((b) step 2).

Figure 5 Le patron de conception export-bind [Kra08]

La variante la plus simple de ce modèle présente un squelette et un stub préalablement créé au développement sur la plateforme du serveur. Cette variante requiert le chargement de code à l'exécution puisque le stub est téléchargé et installé dynamiquement sur la plateforme du client.

Une variante plus élaborée impose que les squelettes et stubs ne soient générés qu'à l'exécution. Cela requiert des techniques de génération de code binaire à la volée (cf. section 3.2.7).

3.2.5 Mobilité logicielle

Certains cas d'utilisation d'Informatique Distribuée font appel à des techniques de migration de code. Ces cas d'utilisation se répondent le plus souvent à des problématiques de répartition de charge même si certains auteurs montrent que ce n'est pas la seule problématique. L'article de Fugetta et al. [FPV98], qui demeure une classification de référence en matière de mobilité logicielle, énumère d'autres cas d'utilisation :

personnalisation de services, extension dynamique des fonctionnalités d'une application, autonomie, tolérance aux pannes, et opérations en mode déconnectés.

[FPV98] classifie en particulier la mobilité de code selon deux catégories :

• Mobilité forte : la mobilité forte se définit par le déplacement d'une unité d'exécution de code avec son état d'exécution, c'est-à-dire la pile et le pointeur d'instructions.

• Mobilité faible : seul le code et des données d'initialisation sont transférés. Dans les données transférées, il est possible d'inclure les données décrivant l'état de l'exécution lors de son arrêt sur la plateforme d'origine, ce qui permet à l'unité d'exécution de reprendre dans un état applicatif similaire sur la plateforme cible. Cet état "faible" décrit l'état de l'application sans lien direct avec la pile d'exécution. De ce fait, la mobilité faible peut se rapprocher de la mobilité forte dans ses conséquences.

Les techniques de mobilité faible sont communes aux techniques des intergiciels distribués traditionnels tels que Java RMI, CORBA, DCOM, etc. Elles permettent les patrons de conception "Remote Proxy" et "export-bind". La différence majeure entre les systèmes de code mobiles et les intergiciels distribués traditionnels est la notion de localisation du code pour le développeur d'application (cf. Figure 6). Les systèmes traditionnels tendent à rendre la localisation des objets distribués transparente alors que les systèmes de code mobile la rendront apparente. Dans ces derniers, le développeur nommera explicitement les plateformes sur lesquels il souhaite déplacer des unités mobiles. En effet, les applications visées sont l'optimisation du déploiement de ces unités pour les besoins énumérés plus haut.

Figure 6 Systèmes distribué traditionnel et systèmes de code mobile [FPV98] L'Informatique Pervasive, par ses besoins d'adaptation des applications embarquées à leur environnement fait souvent appel aux techniques de Code à la Demande [Don07] qui s'apparente à des techniques de mobilité faible, précisément de la mobilité de code sans état. Ces techniques étendent les fonctionnalités d'une application.

3.2.6 Protocoles binaires et protocoles humainement lisibles

Des protocoles binaires sont spécifiés par certaines technologies pour la communication répartie (par exemple, DCOM, Java RMI). Ces protocoles binaires définissent un format d'écriture d'opération et de paramètres attachés à un langage de programmation. Les identifiants d'objets sont des numéros, les types d'objets sont des classes identifiées par un code binaire (codebase) qui indique pour chaque partie le même chargeur de classe (classloader), les arguments de la méthode sont de même décrits de manière binaire selon un code allégé par une connaissance préalablement partagée entre les deux parties. L'aspect binaire rend le protocole compact. Par leur optimisation et leur compacité, ces protocoles binaires sont particulièrement performants pour échanges entre clients et serveurs

partageant un même langage de programmation. En revanche, ils sont moins adaptés pour des échanges entre parties aux langages hétérogènes.

Au contraire de technologies attachées à des langages de programmation définissant un format binaire pour la communication répartie, SOAP définit un format d'échange humainement lisible à base de XML agnostique au choix des technologies de plateformes. Les technologies utilisant cette approche sont en vogue depuis le début des années 2000 : Web Services, UPnP, etc.

Utiliser un protocole humainement lisible tel XML signifie que les paramètres d'appels de méthodes ou de procédures distants doivent être convertis en chaines de caractères, ce qui requiert une charge de traitement supplémentaire sur le client comme sur le serveur. Des pertes de performances sont montrées par des travaux de comparaison ([DaZ02] mentionne un facteur 2 à 30 selon les tests effectués). Le gain d'interopérabilité entre plateformes est apporté par les protocoles humainement lisibles au détriment de la performance.

[MSSG07] énumère de nombreux travaux existant qui comparent les protocoles d'administration basés sur la technologie XML et les autres technologies telles que SNMP. Tous les travaux montrent que les performances des protocoles basés XML sont pauvres en regard de celles de protocoles tels que SNMP [SNMP02] ou Corba [CORB95] dont les protocoles de communication spécifient des messages plus compacts – SNMP BER, Corba RMI/IIOP.

3.2.7 Réingénierie de code binaire

Réécrire le code binaire d'une application est une technique qui permet de rendre certaines transformations transparentes pour le développeur. La réingénierie de code binaire peut être utile pour le test et l'analyse de code, pour les technologies masquant l'implémentation d'un aspect complexe dans les applications (ici l'aspect distribué). La réingénierie automatique du code binaire est plus pertinente que celle du code source en particulier lorsque l'implémentation de l'aspect complexe dépend du contexte de déploiement ou d'exécution de l'application. Cette réingénierie consiste en la détection d'un schéma de code visé et en la transformation des occurrences détectées suivant un autre schéma.

Le code binaire de la technologie Microsoft COM est un standard portable pour partie. En particulier, les interfaces des composants COM, décrites dans le langage MIDL (Microsoft Interface Description Language), se détectent dans le code binaire [HuS99-2]. Il est alors possible de réécrire le code binaire d'appel des interfaces afin d'ajouter des stubs et des skeletons pour la distribution des appels selon la technologie DCOM.

La réingénierie de code binaire Java est encore plus aisée puisque le code Java compilé, appelé bytecode est destiné à l'interprétation par une Machine Virtuelle Java standardisée (JVM). Des technologies de réingénierie de bytecode Java sont même disponibles : ASM [BLC02] a récemment supplanté l'usage d'Apache BCEL (Byte Code Engineering Library) répandu auparavant dans les communautés Java.

Transformer chaque référence d'objet en référence indirecte implique plusieurs changements dans le code binaire de l'application. Par exemple, toutes les créations des objets potentiellement distribués doivent être réécrites en créations de proxys. Les passages par référence de l'objet "this" doivent être transformées en passage de proxy, si les champs d'objets sont utilisés par d'autres objets, l'accès à tous ces champs doivent être remplacés par des "getters" (méthodes d'obtention directe de la valeur des champs) et des "setters" (méthodes de configuration de champs). De telles méthodes peuvent être générées automatiquement pour toute classe d'application Java. Par exemple, si l'application souhaite changer la valeur d'un champ objet.helloString = "hello", le code doit être changé en objet.setHelloString("hello").

3.2.8 Les écueils de l'Informatique Distribuée

Selon Jim Waldo et al. [WWWK94], la vision habituelle de l'Informatique Distribuée comporte généralement des idées fausses. L'idée d'une description uniforme d'objet dans un unique espace d'adresse est fausse. Les applications distribuées doivent distinguer l'espace d'adresse local et l'espace d'adresse distant lorsque la communication entre objets intervient entre des processus distincts sur la même machine ou sur des machines différentes.

Une situation intermédiaire entre l'Informatique locale et l'Informatique Distribuée peut être définie lorsque plusieurs processus distincts fonctionnent sur la même machine. Dans ce cas, les erreurs de communication ne sont plus un problème car les erreurs affectent simultanément tous les processus au même moment.

Les auteurs constatent que beaucoup d'efforts sont portés sur les langages de programmation afin de rendre l'écriture d'applications distribuées et l'écriture d'applications locales identiques pour le développeur. L'erreur principale vient de la pensée que les difficultés de l'Informatique Distribuée sont portées par le langage de programmation qui tentent de cacher l'accès à des mémoires différentes et la gestion de la concurrence. Pour les auteurs, les difficultés proviennent en réalité des propriétés intrinsèques de la communication distante, difficultés qui sont représentées par les 8 hypothèses fallacieuses en Informatique Distribuée selon Peter Deutsch [Rot06] :

• Le réseau est fiable.

• La latence est nulle.

• La bande passante est infinie.

• Le réseau est sécurisé.

• La topologie est fixe.

• Un seul administrateur existe.

• Le coût de transport est nul.

• Le réseau est homogène.

La latence du réseau est probablement le problème essentiel qui distingue l'Informatique usuelle de l'Informatique distribuée. Un appel distant est beaucoup plus lent qu'un accès mémoire. Afin de limiter l'impact du problème, il est important de limiter le nombre d'appels distants. Si les appels distants sont limités en nombre, il faut aussi les limiter en taille puisque la bande passante n'est jamais infinie. La bande passante dépend des réseaux traversés et des taux d'erreur associés.

Les erreurs dues à la communication en réseau (fiabilité, topologie) sont plus ou moins fréquentes selon les réseaux. Les équipements se joignent et quittent le réseau sur les réseaux dynamiques. Les machines peuvent aussi changer de localisation sur Internet. Les équipements de connectivité connaissent des erreurs même si elles sont peu fréquentes. Ces cas impliquent la nécessité de fiabiliser les échanges de messages et de préparer les appels de méthodes distantes aux cas d'erreurs. Certains procédés d'abstraction permettent de rendre les changements invisibles aux applications. Par exemple, le protocole DNS permet de rendre transparent les changements d'adresses IP.

Les autres problèmes à adresser lors de la répartition d'une application sont les besoins de sécurité de communication, d'adaptabilité en fonction des reconfigurations de l'environnement par différents administrateurs, le coût des solutions techniques mises en place pour permettre les traitements distribués et pallier les problèmes cités.

Une erreur est de penser que le réseau est homogène. Le succès des standards à base de technologies web (Web Services par exemple) montre que ce point est sérieusement pris

en compte par les applications distribuées d'aujourd'hui puisque l'interopérabilité de standard protocolaires humainement lisibles est privilégiée par rapport à la performance apportée par d'autres solutions. Toutefois, l'utilisation de technologies communément admises et indépendantes des langages de programmation ne suffit pas. De nombreux standards existent toujours, de nombreuses variantes existent pour chacun et, de surcroît, deux implémentations d'une même technologie peuvent encore fonctionner selon des comportements différents. Et si l'hétérogénéité est un problème important en Informatique Distribuée, il est encore plus saillant dans l'Informatique Pervasive (cf. section 4).

La transparence de l'aspect distribué dans le langage de programmation est possible. De nombreux langages et plusieurs approches logicielles rendent les interfaces d'objets distants presqu'identiques aux interfaces d'objets locaux. Cependant, les développeurs se doivent de garder en tête la distinction entre les concepts d'appel locaux et d'appel distants.