Systèmes et Applications Répartis

Exercice 1 - Questions diverses Question 1 - Comparaison entre RPC et RMI Voici le tableau comparatif complété selon les éléments de la correction officielle : Critère RPC RMI Plateforme (1/multi) Multiplateforme Multiplateforme 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, RPC, RMI, JMS · 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é selon les éléments de la correction officielle :

Critère RPC RMI
Plateforme (1/multi) Multiplateforme Multiplateforme
Langage de programmation C, C++, etc. (Multi-langages) Java
Utilitaire d’IDL rpcgen javac (et rmic historiquement)
Protocole réseau TCP/IP, UDP TCP/IP, HTTP, IIOP
Serveur de nommage portmapper (ou rpcbind) rmiregistry
Extra Base de NFS RMI dynamique (chargement de classes)

Question 2 - Commande Unix pour vérifier les services RPC

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

Exemples communs de services :

  • portmap (ou rpcbind)
  • nfs (Network File System)
  • mountd

Question 3 - Explication des commandes

a) rpcinfo –u localhost 53667767 Cette commande appelle la procédure 0 du programme RPC portant le numéro d'identification 53667767 sur la machine locale (localhost) en utilisant le protocole UDP (-u). Cela agit comme une sorte de "ping" pour vérifier le bon fonctionnement du réseau et s'assurer que ce service spécifique est bien opérationnel et répond aux requêtes.

b) Commande Java RMI

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

Cette commande lance le serveur RMI (la classe chat.server.RemoteServerImpl avec l'argument MonNomServer). Les drapeaux (flags) effectuent les actions suivantes :

  • -Djava.security.policy=../tous.policy : Indique à la machine virtuelle Java (JVM) le fichier définissant la politique de sécurité à utiliser (nécessaire pour que RMI puisse télécharger du code dynamiquement en toute sécurité).
  • -Djava.rmi.server.codebase=[http://machine.ensi.rnu.tn/](http://machine.ensi.rnu.tn/) : Spécifie l'URL (le codebase) où les clients pourront télécharger les classes nécessaires (comme les stubs) pour interagir avec ce serveur.

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

La ligne de code correcte est la proposition e) : RobjInt q = (RobjInt)Naming.lookup(...);

Note de l'enseignant : La méthode Naming.lookup() retourne un objet de type générique java.rmi.Remote. Il est impératif de faire un transtypage (cast) vers l'interface distante (RobjInt), et non vers la classe d'implémentation (Robj), car le client ne manipule que le "stub" (souche) qui implémente l'interface, et non l'objet serveur lui-même.

Question 5 - Concepts de messagerie JMS

Le tableau suivant fait correspondre les interfaces de l'API JMS (Java Message Service) aux deux modèles de communication :

JMS parent Modèle Point-to-Point (P-to-P) Modèle Publish-Subscribe (Pub-Sub)
Destination Queue Topic
Session QueueSession TopicSession
MessageProducer QueueSender TopicPublisher (La correction source indique "TopicSender", mais dans l'API standard JMS 1.1, c'est TopicPublisher. Nous conservons la logique de la source tout en soulignant ce point).
MessageConsumer QueueReceiver TopicSubscriber (La correction source indique "TopicReceiver", le terme exact de l'API est TopicSubscriber).

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

  • Réception synchrone : La délivrance se fait en mode pull (tirer). Le client fait une demande explicite pour consommer un message (par exemple via un appel bloquant receive()) et attend qu'un message soit disponible.
  • Réception asynchrone : La délivrance se fait en mode push (pousser). La consommation est implicite, généralement gérée par un écouteur (un Listener comme MessageListener en JMS) qui est invoqué automatiquement par le MOM dès l'arrivée d'un message.

Question 7 - Relation entre JMS, JORAM et Scalagent

  • JMS (Java Message Service) : Est une spécification (une API) de Java pour les intergiciels orientés messages (MOM). Ce n'est pas un logiciel exécutable, mais un standard.
  • JORAM : Est une implémentation open-source spécifique de l'architecture JMS. C'est le serveur MOM réel qui fait le travail distribué.
  • Scalagent : Est l'entreprise/le projet qui développe et maintient JORAM. Leur implémentation repose sur une architecture d'agents distribués.

Exercice 2 - RPC (Remote Procedure Call)

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

Note de l'enseignant : Le texte source contient des erreurs de syntaxe dans le code RPCL fourni (comme struct string chaine <25>). Le code ci-dessous a été réparé pour être valide vis-à-vis du compilateur rpcgen, tout en respectant strictement l'architecture (deux programmes distincts RVols et GVols) voulue par l'énoncé.

Fichier des types communs (implicite ou à inclure) et RTable.x (pour les clients) :

/* RTable.x */
typedef string chaine<25>;

/* Interface pour la consultation par les clients */
program RVols {
    version RVol_VERS {
        void VNULL(void) = 0;
        /* La source utilise Time, supposons un type abstrait défini par un typedef entier */
        long FINDH(chaine Ref_vol) = 1;
    } = 1;
} = 0x34567891;

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

/* Tableau.x */
typedef string chaine<25>;

struct Info {
    chaine RefVol;
    chaine campagnie;
    chaine dest_dep;
    long heure;
    chaine etat;
};

/* Interface pour la gestion par les employés */
program GVols {
    version GVols_VERS {
        void VNULL(void) = 0;
        int ADDROW(Info ligne) = 1;
        int DELETEROW(chaine Ref_vol) = 2;
        long FINDH(chaine Ref_vol) = 3;
    } = 1;
} = 0x23456789;

Question 2 - Rôle du fichier RTable_xdr.c

Le fichier RTable_xdr.c est généré automatiquement par l'utilitaire rpcgen à partir du fichier RTable.x. Il contient les filtres XDR (eXternal Data Representation). Son rôle est de gérer la sérialisation (encodage) et la désérialisation (décodage) des structures de données complexes afin de garantir qu'elles soient représentées de manière standardisée sur le réseau, indépendamment de l'architecture matérielle du client ou du serveur (par exemple, pour gérer le boutisme / endianness).

Question 3 - Prototype de la fonction FindH côté serveur

Le prototype généré pour le serveur sera de la forme suivante en C :

Time * findh_1_svc(string *vol, struct svc_req *rqstp);

Explication : _1 correspond au numéro de version de l'interface (ici la version 1). Le suffixe _svc indique qu'il s'agit du code exécuté côté serveur (le skeleton ou répartiteur). Le pointeur de type struct svc_req contient les informations relatives à la requête de transport (contexte d'appel).

Question 4 - Appel distant de la fonction FindH par un client

Note de l'enseignant : Le code source de l'examen comportait une erreur de syntaxe C majeure. Voici le code C réparé et fonctionnel.

Pour qu'un client interroge le serveur distant :

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

CLIENT *clnt;
Time *t;
char *server = "nom_de_la_machine_serveur";
char *vol = "TU_21537";

/* 1. Création de la poignée client (connexion au serveur) */
clnt = clnt_create(server, RVols, RVol_VERS, "tcp");
if (clnt == NULL) {
    clnt_pcreateerror(server);
    exit(1);
}

/* 2. Appel de la procédure distante (noter le passage par adresse) */
t = findh_1(&vol, clnt);
if (t == NULL) {
    clnt_perror(clnt, "Appel distant a échoué");
}

Question 5 - Ajout de FindBox et appel combiné par le client

Pour offrir la nouvelle fonction FindBox sans casser la compatibilité avec les clients existants, on crée une version 2 du programme dans le fichier RTable.x :

/* RTable.x modifié */
program RVols {
    version RVol_VERS {
        void VNULL(void) = 0;
        long FINDH(string Ref_vol) = 1;
    } = 1;

    version RVol_VERS2 {
        void VNULL(void) = 0;
        long FINDH(string Ref_vol) = 1;
        int FINDBOX(string dest) = 2;
    } = 2;
} = 0x34567891;

Explication de la solution : En regroupant FINDH et FINDBOX dans la version 2, un client moderne a besoin de se connecter une seule fois (en ciblant la version 2) pour utiliser les deux fonctions conjointement, évitant ainsi la surcharge de multiples connexions TCP. Côté serveur, l'implémentation de findh_2_svc peut simplement faire appel à la fonction métier locale déjà utilisée par findh_1_svc pour factoriser le code.

Côté client, le code devient :

CLIENT *clnt = clnt_create(server, RVols, RVol_VERS2, "tcp");
char *vol = "TU_2137";
char *dest = "Lyon";

Time *t = findh_2(&vol, clnt);
int *nb = findbox_2(&dest, clnt);

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

Question 1 - Conditions d'accès distant et concurrent

Pour permettre un accès distant, les méthodes ReserverChambre et AnnulerReservation doivent être déclarées public et lancer l'exception java.rmi.RemoteException. Pour assurer un accès concurrent de manière sécurisée (éviter que deux clients ne modifient les données en même temps et causent une incohérence), elles doivent être déclarées avec le mot-clé synchronized dans la classe d'implémentation côté serveur.

Question 2 - Interface du service distant

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 - Passage par valeur de l'entité Information

Conditions pour Information : La classe Information (ainsi que tout autre objet qu'elle encapsule, comme des dates ou des sous-structures) doit implémenter l'interface java.io.Serializable. C'est obligatoire pour que RMI puisse convertir l'objet en flux d'octets et l'envoyer sur le réseau.

Fichiers .class nécessaires côté serveur : Le serveur aura besoin de :

  • ReservationChambre.class (l'interface)
  • ReservationChambreImpl.class (son implémentation)
  • Information.class (la définition de l'objet)
  • InformationImpl.class (si une implémentation distincte existe, bien que pour de simples objets métiers sérialisables, l'interface n'est pas strictement obligatoire contrairement aux objets distants).

Question 4 - Passage par référence de l'entité Information

Conditions pour Information : Pour être passée par référence, l'entité Information devient elle-même un objet distant. Elle doit :

  1. Avoir une interface qui hérite de java.rmi.Remote.
  2. Son implémentation doit hériter de java.rmi.server.UnicastRemoteObject ou bien être exportée manuellement via UnicastRemoteObject.exportObject(this, 0).

Fichiers .class nécessaires côté serveur : Le serveur aura besoin de :

  • ReservationChambre.class
  • ReservationChambreImpl.class
  • Information.class (L'interface distante de Information, car le serveur recevra un stub distant, pas l'implémentation du client).

Question 5 - Meilleur choix d'implémentation

Le choix de la question 3 (passage par valeur / sérialisation) est le meilleur. Justification : Les données d'une réservation (nom, nombre de nuitées, etc.) sont statiques. Il est beaucoup plus performant d'envoyer une copie de ces informations au serveur lors de l'appel. Le serveur pourra alors interroger ces propriétés localement en mémoire. Avec le passage par référence, chaque appel à une méthode comme inf.getNom() obligerait le serveur à refaire une requête réseau vers le client pour obtenir la valeur, ce qui dégraderait fortement les performances (latence réseau) et créerait une dépendance dangereuse à la connexion du client.

Question 6 - Code du client ReservationClient

Note de l'enseignant : Le code source contenait des fautes de frappe bloquant la compilation Java (variables mal nommées, confusions classe/instance). Le code a été corrigé ci-dessous.

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 distant
            ReservationChambre reservationObj = 
                (ReservationChambre) Naming.lookup("rmi://machineServeur/Reservation");
            
            // 2. Création d'une instance de l'objet Information
            // Paramètres : nom, nb_chambres, nb_nuitees, prix_nuit, date_arrivee
            Information inf = new InformationImpl("Dupont", 1, 3, 75.0, "15/07/2014");
            
            // 3. Appel de la méthode distante de réservation
            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.out.println("Erreur dans ReservationClient : " + e.getMessage());
            e.printStackTrace();
        }
    }
}

Question 7 - Contexte d'exécution de la méthode getDate()

(Cette question, présente dans la grille de correction officielle, vise à tester votre compréhension de la sérialisation).

Si l'on utilise la méthode de la question 3 (passage par valeur), la méthode getDate() appelée dans ReserverChambre sera exécutée par la JVM du serveur. Justification : L'objet Information a été entièrement sérialisé sur le réseau. Le serveur a reçu les octets et a instancié un clone local en mémoire. Lorsque le serveur invoque une méthode sur cet objet, l'exécution se fait donc localement, dans l'environnement du serveur, sans aucun trafic réseau supplémentaire.

Méthode

Comment aborder ce type d'épreuve (Systèmes et Applications Répartis) :

  1. Différencier l'architecture RPC vs RMI : La clé de ce cours est de comprendre que RPC manipule des fonctions écrites en C avec des procédures définies dans un fichier .x (IDL), compilé par rpcgen. RMI est purement orienté objet (Java), repose sur des interfaces héritant de Remote et gère le transfert de code via le réseau (RMI registry et codebase).
  2. Maîtriser les modes de passage : En RMI, le piège classique est la différence entre le passage par valeur (nécessite Serializable, copie complète de l'état, exécution locale des accesseurs) et le passage par référence (nécessite Remote et UnicastRemoteObject, passe un stub, requiert des appels réseau pour chaque méthode). Choisissez la valeur pour les objets métiers (DTOs) et la référence pour les services ou gestionnaires.
  3. Vérifier le code généré : Ne confondez pas l'interface métier avec le comportement réseau. Une méthode dans le .x en RPC retournera un pointeur (ex: Time*) dans l'implémentation C générée, même si elle renvoyait un type basique dans la déclaration RPCL.
  4. Syntaxe : Soyez très vigilants aux exceptions en Java (throws RemoteException est impératif pour toute méthode d'une interface distante) et aux transtypages lors de l'utilisation de registres de nommage (comme Naming.lookup).

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