Systèmes et Applications Répartis

Exercice 1 : Questions diverses Question 1 - Comparaison entre RPC et RMI Voici le tableau comparatif complété des mécanismes de communication RPC (Remote Procedure Call) et RMI (Remote Method Invocation). Caractéristique RPC RMI Plateforme (1/multi) Multi-plateformes Multi-plateformes (via la JVM) Langage de programmation C, C++, etc.

D'après le document Systèmes et Applications Répartis

Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Systèmes et Applications Répartis

Document source

Systèmes et Applications Répartis

Programming, Distributed Systems · PDF · 10 pages · 2013

Afficher l'aperçu du document

Consulter le document original →

Exercice 1 : Questions diverses

Question 1 - Comparaison entre RPC et RMI

Voici le tableau comparatif complété des mécanismes de communication RPC (Remote Procedure Call) et RMI (Remote Method Invocation).

Caractéristique RPC RMI
Plateforme (1/multi) Multi-plateformes Multi-plateformes (via la JVM)
Langage de programmation C, C++, etc. (multi-langages) Java (spécifique à Java)
Utilitaire d'IDL rpcgen javac (et historiquement rmic)
Protocole réseau TCP/IP, UDP TCP/IP, HTTP, IIOP
Serveur de nommage portmapper (ou rpcbind) rmiregistry
Extra Base du système de fichiers NFS Permet le chargement dynamique de classes

Question 2 - Commande de vérification des services RPC

La commande Unix qui permet de lister les services RPC disponibles sur un serveur est : rpcinfo -p

Exemples communs de services RPC :

  1. portmap (ou rpcbind) : le service de mappage des ports lui-même.
  2. nfs (Network File System) : pour le partage de fichiers en réseau.
  3. mountd : le démon de montage pour NFS.

Question 3 - Explication des commandes système

a) rpcinfo –u localhost 53667767 Cette commande effectue un appel (une sorte de "ping" applicatif) à la procédure 0 du programme RPC portant le numéro d'identification 53667767 sur la machine locale (localhost), en utilisant le protocole UDP (indiqué par le drapeau -u). Cela permet de vérifier si ce service spécifique est enregistré et répond correctement sur le réseau.

b) Commande d'exécution Java RMI

java -Djava.security.policy=../tous.policy \
     -Djava.rmi.server.codebase=http://machine.ensi.rnu.tn/ \
     chat.server.RemoteServerImpl MonNomServer

Cette commande lance la classe serveur chat.server.RemoteServerImpl (qui prend ici l'argument MonNomServer) au sein de la Machine Virtuelle Java. Les drapeaux -D définissent des propriétés système vitales pour RMI :

  • java.security.policy=../tous.policy : Spécifie le fichier de politique de sécurité à utiliser, obligatoire pour autoriser le téléchargement dynamique de code en RMI.
  • java.rmi.server.codebase=[http://machine.ensi.rnu.tn/](http://machine.ensi.rnu.tn/) : Indique l'URL à partir de laquelle les clients pourront télécharger les classes (les stubs) nécessaires pour interagir avec ce serveur.

Question 4 - Obtention d'une référence à l'objet distant

La bonne ligne de code est la réponse e) :

RobjInt q = (RobjInt)Naming.lookup(...);

Explication : La méthode Naming.lookup() retourne un objet de type générique java.rmi.Remote. En RMI, le client manipule toujours l'interface distante (RobjInt), et non l'implémentation concrète de l'objet (Robj). Il faut donc effectuer un transtypage (cast) vers l'interface RobjInt.

Question 5 - Concepts de messagerie JMS

Voici le tableau complété pour les deux modèles de l'API Java Message Service (JMS) :

JMS parent Modèle Point-to-Point (P2P) Modèle Publish-Subscribe (Pub/Sub)
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicSender
MessageConsumer QueueReceiver TopicReceiver

Question 6 - Réception synchrone vs asynchrone pour un client MOM

  • Réception synchrone : Le client demande explicitement le message (mode "pull"). L'appel est bloquant : le thread du client s'arrête et attend jusqu'à ce qu'un message soit disponible (ex: utilisation de la méthode receive()).
  • Réception asynchrone : La distribution est gérée par le fournisseur (mode "push"). Le client enregistre un écouteur (un MessageListener avec une méthode onMessage()) et continue son exécution. Le fournisseur invoque automatiquement l'écouteur lorsqu'un message arrive.

Question 7 - Relation entre JMS, JORAM et Scalagent

  • JMS (Java Message Service) : C'est une spécification (une API standard de Java) qui définit comment les clients doivent interagir avec un intergiciel orienté messages (MOM).
  • JORAM : C'est une implémentation open-source concrète de l'API JMS.
  • Scalagent : C'est l'entreprise/l'organisation (Scalagent Distributed Technologies) qui a développé et maintient l'implémentation JORAM basée sur une architecture d'agents distribués.

Exercice 2 : RPC

Question 1 - Interfaces RPCL (Tableau.x et RTable.x)

Note : Le code RPCL fourni dans le corrigé source contient de graves erreurs de syntaxe empêchant la compilation par rpcgen (comme struct string chaine <25> ou Typedef Info ligne ;). Voici la version syntaxiquement correcte et fonctionnelle.

Interface pour les employés (Tableau.x) :

/* Fichier Tableau.x - Gestion complète des vols */
typedef string chaine<25>;
/* Définition du type Time (souvent représenté par un long UNIX) */
typedef long Time;

struct ligne {
    chaine RefVol;
    chaine compagnie;
    chaine dest_dep;
    Time heure;
    chaine etat;
};

typedef struct ligne Info;

program GVOLS {
    version GVOLS_VERS {
        void VNULL(void) = 0;
        int AddRow(Info ligne) = 1;
        int DeleteRow(string Ref_vol) = 2;
        Time FindH(string Ref_vol) = 3;
    } = 1;
} = 0x23456789;

Interface pour les clients (RTable.x) :

/* Fichier RTable.x - Consultation des vols uniquement */
typedef string chaine<25>;
typedef long Time;

program RVOLS {
    version RVOLS_VERS {
        void VNULL(void) = 0;
        Time FindH(string Ref_vol) = 1;
    } = 1;
} = 0x34567891;

Question 2 - Utilité du fichier RTable_xdr.c

Le fichier RTable_xdr.c est généré automatiquement par le compilateur rpcgen à partir du fichier RTable.x. Il contient les fonctions de filtres XDR (eXternal Data Representation). Ces fonctions servent à sérialiser (encoder) et désérialiser (décoder) les structures de données complexes pour qu'elles puissent transiter de manière standardisée sur le réseau, indépendamment de l'architecture des machines clientes et serveurs (gestion du boutisme/endianness, taille des entiers, etc.).

Question 3 - Prototype de la fonction FindH dans le serveur

En C avec RPC, la fonction implémentée côté serveur se voit attribuer un suffixe indiquant sa version (_1) et le fait qu'elle s'exécute côté serveur (_svc). Les paramètres d'entrée sont toujours passés par pointeur.

Time * findh_1_svc(char **ref_vol, struct svc_req *rqstp);

(Le corrigé de la source utilise string *vol, mais en C standard généré par rpcgen, un type RPC string se traduit par un char *. Son passage par référence donne donc un pointeur sur pointeur de caractère : char **.)

Question 4 - Appel distant depuis un client

Voici le code C qu'un client doit exécuter pour se connecter et appeler la fonction :

#include "RTable.h" /* Généré par rpcgen */

void consulter_heure(char *host) {
    CLIENT *clnt;
    Time *resultat;
    char *ref_vol = "TU_21537";

    /* 1. Création du client et connexion au serveur RPC */
    clnt = clnt_create(host, RVOLS, RVOLS_VERS, "tcp");
    if (clnt == NULL) {
        clnt_pcreateerror(host);
        exit(1);
    }

    /* 2. Appel distant */
    resultat = findh_1(&ref_vol, clnt);
    if (resultat == (Time *) NULL) {
        clnt_perror(clnt, "Erreur lors de l'appel RPC");
        exit(1);
    }

    /* Traitement du résultat ici */
    
    clnt_destroy(clnt);
}

Question 5 - Modification du serveur pour FindBox

Pour ajouter une fonction sans casser la compatibilité avec les clients existants, il faut créer une nouvelle version (version 2) dans le programme RPC.

Nouveau RTable.x :

typedef string chaine<25>;
typedef long Time;

program RVOLS {
    version RVOLS_VERS {
        void VNULL(void) = 0;
        Time FindH(string Ref_vol) = 1;
    } = 1;

    version RVOLS_VERS2 {
        void VNULL(void) = 0;
        Time FindH(string Ref_vol) = 1;
        int FindBox(string name) = 2;
    } = 2;
} = 0x34567891;

Explication de l'appel client : Un client souhaitant utiliser les deux fonctions simultanément créera une instance (un handle) de communication CLIENT ciblant spécifiquement la version 2 (RVOLS_VERS2). Il pourra ainsi appeler consécutivement findh_2(&ref_vol, clnt) et findbox_2(&destination, clnt) au travers de la même connexion. Côté serveur, pour éviter la duplication de code, la fonction findh_2_svc appellera simplement la logique métier de findh_1_svc.

Exercice 3 : Réservation de chambre à distance par RMI

Question 1 - Conditions pour un accès distant et concurrent

Pour assurer un accès distant via RMI, les méthodes ReserverChambre et AnnulerReservation doivent lever l'exception java.rmi.RemoteException. Pour gérer l'accès concurrent (éviter que deux clients réservent la même chambre au même moment), ces méthodes doivent être déclarées avec le mot-clé synchronized dans l'implémentation du serveur, garantissant l'exclusion mutuelle.

Question 2 - Interface du service RMI

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ReservationChambre extends Remote {
    public int ReserverChambre(Information inf) throws RemoteException;
    public boolean AnnulerReservation(int ref) throws RemoteException;
    public Information DetailsReservation(int ref) throws RemoteException;
}

Question 3 - Paramètre passé par valeur (Information)

  • Condition : La classe Information doit implémenter l'interface java.io.Serializable. Ainsi, son état pourra être converti en flux d'octets et transmis sur le réseau.
  • Fichiers class nécessaires côté serveur : Le serveur doit posséder ReservationChambre.class (l'interface), ReservationChambreImpl.class (son implémentation), mais aussi Information.class et InformationImpl.class afin de pouvoir désérialiser l'objet reçu et accéder à ses données.

Question 4 - Paramètre passé par référence

  • Modifications sur l'objet : L'objet Information devient un objet distant. Il doit implémenter une interface qui hérite de java.rmi.Remote et chaque méthode exposée doit lever RemoteException. Son implémentation doit soit hériter de java.rmi.server.UnicastRemoteObject, soit être exportée manuellement via UnicastRemoteObject.exportObject(this, 0).
  • Fichiers class nécessaires côté serveur : Le serveur a seulement besoin de ReservationChambre.class, ReservationChambreImpl.class et de l'interface Information.class. Il n'a pas besoin de InformationImpl.class car il recevra uniquement un stub (une référence réseau) pointant vers l'objet résidant sur la machine cliente.

Question 5 - Meilleur choix d'implémentation

Le choix de la question 3 (passage par valeur via Sérialisation) est nettement meilleur. Justification : Les données encapsulées dans l'objet Information (nom, dates, nombre de chambres) représentent un état statique, un formulaire. Si on le passait par référence, chaque fois que le serveur voudrait lire un champ (ex: inf.getNom()), cela déclencherait un nouvel appel réseau vers le client, augmentant drastiquement la latence, la charge réseau, et créant un point de défaillance si le client se déconnecte pendant le traitement. Le passage par copie permet au serveur de traiter la réservation localement et rapidement.

Question 6 - Code du client RMI

Note : Le code du corrigé original était truffé d'erreurs Java (variables non déclarées, fautes de frappe, classes inexistantes). Voici le code corrigé, compilable et fonctionnel.

import java.rmi.Naming;

public class ReservationClient {
    public static void main(String args[]) {
        try {
            // 1. Récupération d'un stub sur l'objet serveur via le registre RMI
            ReservationChambre reservationObj = (ReservationChambre) Naming.lookup("rmi://machineServeur/Reservation");
            
            // 2. Création de l'objet Information (qui doit être Serializable)
            // On suppose que InformationImpl implémente l'interface Information
            Information inf = new InformationImpl("Dupont", 1, 3, 85.5, "2014-06-15");
            
            // 3. Appel distant
            int ref = reservationObj.ReserverChambre(inf);
            
            // 4. Affichage du résultat
            if (ref != 0) {
                System.out.println("La référence de réservation est : " + ref);
            } else {
                System.out.println("Echec de la réservation.");
            }
        } catch (Exception e) {
            System.err.println("Erreur côté client : " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Question 7 - Lieu d'exécution de la méthode getdate()

(Bien que cette question n'apparaisse pas explicitement dans l'énoncé source initial, elle figure dans le corrigé fourni).

Si on considère le choix de la question 3 (passage par valeur), la méthode getdate() sera exécutée par la JVM du serveur. Justification : L'objet Information entier a été sérialisé par le client, transmis sur le réseau, et désérialisé par le serveur. Le serveur possède désormais un clone local de l'objet. Lorsqu'il invoque getdate(), l'exécution se fait entièrement dans sa propre mémoire locale, sans aucune communication réseau retour vers le client.

Méthode

Pour réussir un examen de Systèmes et Applications Répartis, la clé n'est pas d'apprendre du code par cœur, mais de maîtriser la mécanique réseau sous-jacente.

  • Identifiez toujours où s'exécute le code : Face à un appel de fonction (RPC ou RMI), demandez-vous systématiquement : "Où suis-je ? Sur le client ou sur le serveur ? Les paramètres voyagent-ils sur le réseau sous forme de copie (valeur) ou de pointeur distant (référence) ?".
  • Faites attention aux contraintes des intergiciels : RMI nécessite la sérialisation, des interfaces spécifiques (Remote) et la gestion des exceptions (RemoteException). Omettre l'un de ces éléments est une erreur fatale. RPC demande une définition stricte des types dans un langage IDL agnostique.
  • Validez vos syntaxes d'examen : Lorsque vous rédigez du code sur papier, concentrez-vous sur la logique des blocs de gestion d'erreur (les try/catch en Java, la vérification des NULL en C) et sur les signatures de connexion (comme Naming.lookup ou clnt_create). C'est là que les correcteurs évaluent votre compréhension de la robustesse des systèmes distribués.

Partager

Commentaires

Aucun commentaire pour le moment. Posez la première question.

Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

← Toutes les révisions