Les systèmes distribuées sont des applications sur des espaces d'adressage différents: ou sur un même hôte ou des hôtes différents. Le problème central de ce type d'applications est la communication entre les différentes applications.
Pour le moment, nous nous sommes contenté de tranférer des données à travers le réseau. Nous l'avons fait les divers protocoles existants: FTP, HTTP, etc. Nous avons vu, qu'avec les sockets, il existe un moyen simple de communication en une application serveur et une application cliente pour transférer des données. Jusqu'à présent, nous ne connaisons que les CGI qui permettent de lancer un programme sur une machine distante.
Les appels de procédures distantes (RPC: Remote Procedure Call ) est un autre moyen pour exécuter des programmes distants. Le but du service RPC est de permettre la programmation aisée de programmes client-serveur sous une forme transparente aux utilisateurs.
L'idée de base est que le paradigme d'appel de procédure est bien connu des programmeurs. Le RPC va permettre de distribuer (ou répartie) le programme principal et les procédures sur des systèmes distants :
Ainsi, au lieu de manipuler directement des sockets, avec le RPC, le programmeur va avoir l'illusion de faire des appels à des procédures locales alors même que ces appels impliquent une communication avec un hôte distant pour envoyer et recevoir des données.
La difficulté essentielle à résoudre pour offrir un service d'appel de procédures à distance est le maintien des propriétés implicitement associées à un appel de procédure local :
Le RPC maintient une certaine transparence : du point de vue du client, la procédure appelée apparaît comme une procédure locale. En fait, la procédure distante appelée est représentée localement par une procédure particulière appelée stub client ou souche cliente : c'est cette procédure locale que le client invoque réellement. Cette souche cliente utilise ensuite le réseau pour contacter la procédure distante implantée par le serveur. Le rôle de la souche cliente est multiple :
Au niveau du serveur, on définit la notion de programme serveur : il est identifié par un numéro de programme non ambigu, un numéro de version qui permet éventuellement la coexistence de versions successives du même programme serveur, et il est constitué d'un ensemble de procédures. Un module particulier contrôle l'accès aux procédures du serveur, le "stub serveur".
La communication entre le stub client et le stub serveur se fait indirectement via deux services :
Les échanges à travers le réseau peuvent se faire soit via TCP, soit via UDP
RPC a été conçu pour la programmation procédurale et ne prévoit rien en ce qui concerne la programmation objet. L'adaptation de RPC à la programmation objet a pour nom RMI (Remote Method Invocation ). S'il existe des RMI qui peuvent s'adapter aux objets Java , le langage Java fournit un RMI qui est propre aux objets Java . Il s'agit ici de travailler dans un monde homogène Java ; contrairement à CORBA qui présuppose une environnement hétérogène, des langages différents.
On appelle objets distants tout objet dont les méthodes sont susceptibles d'être invoquées par une machine virtuelle Java autre que celle sur laquelle réside cet objet. Il s'agit généralement d'une machine distante.
Lors de l'invocation d'une méthode distante, il reste un problème délicat posé par le passage de ses arguments et l'objet éventuellement retourné par celle-ci.
A TERMINER
L'ensemble des classes qui prennent en charge la gestion des RMI sont regroupées dans les packages java.rmi, java.rmi.server, java.rmi.registry et java.rmi.dgc.
| Package java.rmi | package pour la gestion client | Package java.rmi.server | package pour la gestion du serveur | Package java.rmi.registry | package pour localiser les objets distants | Package java.rmi.dgc | récupération de mémoire |
Avant d'entrer dans les détails, voici l'exemple fournit dans le tutorial de jdk.
Un objet distant se doit d'implanter une ou plusieurs interfaces distantes ; une interface distante est une interface qui étend directement ou indirectement l'interface java.rmi.Remote.
L'interface java.rmi.Remote ne définit aucune méthode propre, c'est une interface vide qui ne sert qu'à identifer les objets suscpetibles d'être des objets distants.
L'ensemble des exceptions que l'invocation d'une méthode distante peut générée est regroupé dans la classe java.rmi.RemoteException et toutes les méthodes implantant cette interface se doivent de renvoyer cette exception.
public interface Hello extends java.rmi.Remote { String sayHello() throws java.rmi.RemoteException; }
A présent, nous allons définir une classe qui implante cette interface ce qui va nous permettre de définir (plus tard) des objets distants qui seront des instances de cette classe. Cette classe doit être une sous classe de la classe java.rmi.server.UnicastRemoteObject.
Sans plus de détails, disons que cette classe java.rmi.server.UnicastRemoteObject fournit un ensemble de méthodes qui vont permettre l'invocation des méthodes distantes.
public class HelloImpl extends java.rmi.server.UnicastRemoteObject implements Hello {
Il nous faut, maintenant, implanter la méthod sayHello de l'interface Hello définit précédemment.
C'est la seule méthode (d'après notre interface) qui peut être invoqué par le client. Elle se contente de retourner au client la chaîne de caractères "Coucou !". A priori, rien ne distingue cette méthode des autres méthodes que l'on pourrait fournir avec cette classe. Seule son appartenance à l'interface Hello lui confère un statut particulier.
public String sayHello() throws java.rmi.RemoteException { return "Coucou !"; }
Il nous reste à terminer l'implantation de la classe en fournissant la méthode main, et le constructeur de notre classe.
Le constructeur de cette classe ne fait rien d'autre que d'appeler le constructeur de la classe mère; ce qui est le comportement par défaut si l'on ne définit aucun constructeur. Pourtant, ici, il va falloir impérativement, la définir à cause de l'exception qu'elle peut engendrer.
La méthode main se charge des opérations à effectuer sur le serveur. On crée tout d'objet une instance de cette classe que l'on enregistre le registre de nommage avec la méthode statique rebind de la classe java.rmi.Naming.
public HelloImpl() throws RemoteException { super(); }
Le lancement du serveur proprement dit se fait en excéutant la programme HelloImpl:
HelloImpl obj = new HelloImpl("Hello"); java.rmi.Naming.rebind("Hello", obj); System.out.println("Le serveur RMI Hello est actif"); \end{alltt}\end{formatprog} C'est cet enregistrement qui met à la disposition du client ces objets. Les clients peuvent alors accès à cet objet ou à la liste des objets disponibles. Voici le code complet de cette classe. \begin{formatprog} \verbatimfile{Programme/rmi/HelloImpl.java} \end{formatprog} \formatsection\subsubsection{Fabriquer les stubs et squelettes}\formattext La partie du codage en \Java\ de notre serveur est à présent fini. IL nous reste à fabriquer les \emph{stubs} et \texttt{squelettes} indispensable aux appels distants. L'utilitaire \texttt{rmic} prend la fichier \texttt{.class} et produit ces \emph{stubs} et \emph{squelettes}: \begin{formatprog}\begin{alltt} \emph{% javac HelloImpl.java} \emph{% rmic HelloImpl} \end{alltt}\end{formatprog} Cette dernière commande produit les deux fichiers \texttt{HelloImpl\_Skel.class} et \texttt{HelloImpl\_Stub.class} \formatsection\subsubsection{Mise en route du serveur}\formattext A présent, tout est prêt pour la mise en route du serveur. Avant toute chose, il faut lancer le programme \texttt{rmiregistry} \begin{formatprog}\begin{alltt} \emph{% rmigestry 2000 & \emph{// sous unix}} \emph{% start rmirestry 2000 \emph{// sous Windows}} \end{alltt}\end{formatprog} Ce programme s'exécute en tâche de fond et se connect par défaut au port 1099. Si ce port est pris, il suffit de spécifier le port que l'on veut utiliser. Ce numéro de port est celui utilisé par le programme \texttt{HelloImpl}: \begin{formatprog}\begin{verbatim} java.rmi.Naming.rebind("//gbm.esil.univ-mrs.fr:2048", obj);
A présent tout est prêt pour recevoir des appels distants !!!
% java HelloImpl & Le serveur RMI Hello est actif %
Le client doit définir l'objet distant dont il veut obtenir une référence. Comment un client peut-il désigné un objet distant ? La syntaxe utilisé est propre de la syntaxe des URL HTTP. Le protocole utilisé est rmi, l'objet réside sur la machine gbm.esil.univ-mrs.fr et le serveur est à l'écoute sur le port 2048 et l'objet recherché s'est fait inscrire sous le nom de Hello. Le client désigne alors cet objet par :
La classe Naming dispose d'une méthode statique lookup qui permet de récupérer cet objet :
rmi://gbm.esil.univ-mrs.fr/Hello
Une fois extrait cet objet, il suffit d'invoquer la méthode sayHello sur cet objet.
Objet o = Naming.lookup("rmi://gbm.esil.univ-mrs.fr:2048/Hello");
Objet o = Naming.lookup("rmi://gbm.esil.univ-mrs.fr/Hello");
\verbatimfile{Programmes/rmi/CallHello.java}
javac Hello.java HelloImpl.java
rmic HelloImpl
start rmiregistry 2048 // Windows rmiregistry 2048 & // Unix
java HelloImpl &
java CallHello