.NET Remoting
vs Java RMI
Introduction
La distribution a toujours été le talon d'Achille de COM avec Distributed COM pour diverses raisons. Tout d'abord, DCOM a toujours été très complexe à mettre en oeuvre, son intégration étroite avec le Framework COM contraint les développeurs à utiliser le langage C++ pour profiter pleinement des APIs avancées (Marshalling, Securité, ..). De plus, DCOM fonctionne dans un environnement non managé rendant complexe son intégration pérenne dans .NET. Il n'en fallait pas plus à Microsoft pour proposer .NET Remoting, remplaçant officiel de DCOM et destiné à combler l'ensemble des lacunes de ce dernier. Le but de cet article est de mieux comprendre ce qu'est .NET Remoting au travers d'un exemple pratique, mais aussi d'analyser les différences entre le Framework Java RMI et les API Remoting. Nous étudierons dans les deux systèmes les étapes nécessaires à la création et à l'invocation d'un objet distribué. Ainsi nous verrons :
Enfin pour finir, nous exploiterons des fonctionnalités avancées de .NET Remoting à travers les Custom Proxy équivalent des dynamics Proxy de Java.
La création des objets distribués La première étape nécessaire pour la mise en place d'un objet distribué consiste à coder l' interface et l'implémentation du serveur. D'ores et déjà, une différence fondamentale oppose les deux Framework : RMI impose la création d'interfaces distribuées alors que Remoting permet d'implémenter directement un serveur dans une classe. Voyons un exemple concret avec le fameux Hello.
Remoting utilise la classe MarshalByRefObject, classe de base pour l'ensemble des objets sérialisés par référence. C'est à dire dont la référence sera sauvée sous la forme d'un flux et transportée à travers le réseau. Ces objets ont les caractéristiques suivantes :
Le code du serveur ressemble à n'importe quel autre objet, il contient des méthodes publiques ou privées et peut dériver d'une interface quelconque, mais ce n'est pas obligatoire. En RMI, toute interface distribuée doit être typée java.rmi.server.Remote et l'implémentation dériver de UnicastRemoteObject, alors qu'en Remoting aucun typage particulier n'est nécessaire. L'enregistrement du serveur
distribué dans l'annuaire Il existe plusieurs paramètres d'enregistrement d'un serveur dans Remoting :
L'enregistrement se fait via l'appel à la méthode : RemotingConfiguration.RegisterWellKnownServiceType() prenant en paramètre :
Vous remarquerez que le nom de la DLL (helloserver) est spécifié pour indiquer au Framework que l'implémentation du serveur (et à fortiori son interface) se trouve dans ce fichier généré lors du processus de compilation.
RMI distingue deux types d'objets distribués : les singletons comme Remoting et les SingleCall en utilisant l'API d'activation. En Java, les objets sont chargés relativement à une machine virtuelle et le marshalling s'opère entre les JVM, la notion de domaine d'application n'existe pas.
Les Channels Le concept de channel a été intégré dans Remoting par Microsoft dans le but de rendre le modèle de distribution complètement paramétrable. L'intérêt est de pouvoir spécifier l'utilisation de différents protocoles en fonction de ces besoins. Il est vrai que la multiplicité des formats de messages et de protocoles de nos jours est un facteur de complexité pour les fournisseurs d'ORB qui doivent sans cesse adapter leurs middleware aux nouveaux formats. A l'heure actuelle, Remoting propose par défaut Http et TCP. Chaque protocole étant souvent associé à un format de représentation des messages, le Framework propose par défaut d'utiliser des Sérialisateurs de messages Soap avec Http et binaire pour TCP. Rien ne vous empêche par exemple de créer vous-même votre propre channel ou utiliser vos formats de messages IIOP (Corba) pour TCP. Concernant RMI, la situation est quelque peu différente. Jusqu'à la version 1.2 du Jdk, les classes de personnalisation existaient mais n'étaient pas publiques car intégrées dans la couche de Transport. Sun, à l'époque, préférait privilégier son protocole maison JRMP basé sur des sockets TCP. Depuis, il est dorénavant possible d'ajouter de nouveaux protocoles ou d'enrichir les protocoles existants (IIOP et JRMP) via la classe RMISocketFactory tel que décrit ici. L'annuaire Rmi passe par un annuaire appelé la RmiRegistry pour stocker les références (Proxies) vers des serveurs distants. Ce service, lui-même objet distribué, doit être lancé manuellement ou dynamiquement avant toute communication entre un client et un serveur. .NET adopte une approche différente dans la mesure où le client communique directement avec le serveur sur un port spécifié entre les deux parties au niveau de l'URL.Dans la pratique, implémenter un Naming Service dans Remoting ne relève pas de l'impossible. Il suffit d'écrire un custom service au dessus d'un channel quelconque et de masquer l'opération d'enregistrement à l'utilisateur pour le rediriger vers notre code. Gageons qu'à l'avenir, Microsoft proposera ce genre de service primordial dans les architectures distribuées. D'ailleurs, dans RMI, la RmiRegistry est elle-même un simple serveur distant. Ecrire le client Le client dans .NET ressemble beaucoup au client RMI. En effet, le but de l'opération consiste à récupérer un Proxy représentant l'objet distant et à invoquer par délégation une méthode sur l'objet distribué. Sur le fond, si RMI utilise le proxy généré par RMIC (générateur de Stub), Remoting utilise un mécanisme beaucoup plus complexe basé sur le concept de TransparentProxy très proche du Dynamic Proxy de Java. C'est pourquoi, dans les étapes de déploiement, vous ne verrez pas de génération de code concernant les Proxy.
La méthode Activator.GetObject() se charge donc de créer à la volée un proxy avec la DLL du serveur. Nous verrons par la suite qu'il est possible de redéfinir le proxy utilisé par défaut afin d'intercepter les appels vers l'objet distribué. Compilation, déploiement et exécution Etudions les différentes étapes de la compilation : 1) Compilation de
l'objet distribué
2) Compilation du MainServer
Le MainServer est compilé en utilisant le fichier DLL précédent car il se charge de son activation lors des appels. 3) Compilation du client
Le client utilise la même DLL lors de la compilation pour permettre à l'Activator de générer le proxy. D'ailleurs, placer le fichier contenant l'implémentation du serveur coté client peut paraître troublant car en RMI le client dispose uniquement d'un Proxy ne contenant que des méthodes techniques (invocation, transport, marshalling). Cela peut poser quelques soucis de sécurité et nuit au concept de couplage client->interface. Voici la fenêtre d'exécution après avoir fait "d:\start MainServer.exe" et lancé le client "Client.exe" :
Ce principe consiste à implémenter un sérialisateur/désérialisateur spécifique de messages par l'intermédiaire d'une classe respectant une interface donnée du Framework. Par défault, Remoting propose deux Custom Formatters: BinaryFormatter associé au channel TCP et SoapFormatter associé au channel Http. Il est aussi possible de les combiner pour optimiser les performances (par exemple SoapBinaire). Les fichiers de configuration
Les fichiers de configuration permettent de paramétrer entièrement le système Remoting par l'intermédiaire d'un fichier XML. L'exemple suivant nous illustre la manière de configurer les serveurs, channels, formatters, etc. ... <configuration> <system.runtime.remoting> <application name="MyFoo"> <service> <wellknown type="Foo, common" objectUri="Foo.soap" mode="Singleton"/> </service> <channels> <channel ref="http server" name="MyHttpChannel" port="9000"> <serverProviders> <provider ref="ip filter" mode="accept"> <filter mask="255.255.255.255" ip="127.0.0.1" /> </provider> <formatter ref="soap" /> </serverProviders> </channel> </channels> </application> <channelSinkProviders> <serverProviders> <provider id="ip filter" type="IPFilter.IPFilterChannelSinkProvider, IPFilterSink" /> </serverProviders> </channelSinkProviders></system.runtime.remoting> </configuration> Remoting.xml Pour charger le fichier suivant dans un client .NET Remoting, il vous suffit d'utiliser la méthode Configure() décrite ci après :
Résumé Microsoft n'a pas réinventé la poudre, les mécanismes classiques de distribution via le principe de Proxy ont encore de beaux jours devant eux. La seule différence provient du fait que RMI propose, par défaut, une génération statique de Proxy et que .NET Remoting se base sur un Transparent Proxy (équivalent du Dynamic Proxy de Java). D'ailleurs, dans le monde JAVA, bon nombre de serveurs d'applications EJB fonctionnent déjà avec le mécanisme de Dynamic Proxy (JBoss.org) qui a tendance à remplacer la génération de code. Concernant les autres caractéristiques de .NET Remoting, nous pouvons remarquer le modèle de développement original basé sur les channels marquant une première ouverture des APIs de Microsoft à des implémentations tierces. A l'heure où nous écrivons ces quelques lignes, peut-être quelqu'un a t-il déjà pensé à implémenter un Message Formatter pour IIOP au dessus du channel TCP ? ;-) Dans de prochains articles, nous aborderons l'aspect intégration avec COM+, la Sécurité, totalement occultée dans cet article, ainsi que le mode de fonctionnement du Ramasse miette distribué basé sur le principe de Leases.
Auteur : Sami Jaber
|