EXPLICATION PÉDAGOGIQUE
COMPLÈTE
RPC et Java RMI - De la Théorie à la Pratique
Pour : Harisson - Génie des Systèmes Intelligents
🎯 OBJECTIF GLOBAL DU COURS
Harisson, ce cours est la SUITE LOGIQUE du cours sur les sockets. Tu as vu comment faire
communiquer deux programmes avec des sockets (en envoyant des messages
manuellement). Maintenant, on va voir comment SIMPLIFIER cette communication en la
faisant ressembler à un simple appel de fonction !
L'ÉVOLUTION LOGIQUE :
Étape 1 (cours précédent) : SOCKETS
→ Tu envoies des messages manuellement
→ Tu gères les connexions, les buffers, les erreurs
→ C'est COMPLIQUÉ mais tu contrôles tout
Étape 2 (ce cours) : RPC / RMI
→ Tu appelles une fonction comme si elle était locale
→ Le système gère AUTOMATIQUEMENT les messages
→ C'est SIMPLE et élégant !
PARTIE 1 : RAPPEL - COMMUNICATION PAR SOCKETS
📌 Ce qu'on faisait avec les sockets
Exemple concret du LoadBalancer que tu as codé :
CLIENT :
1. Socket socket = new Socket("localhost", 8080);
2. OutputStream out = [Link]();
3. [Link](requete); // Envoyer manuellement
4. InputStream in = [Link]();
5. [Link](buffer); // Recevoir manuellement
6. [Link]();
SERVEUR :
1. ServerSocket serverSocket = new ServerSocket(8080);
2. Socket clientSocket = [Link]();
3. InputStream in = [Link]();
4. [Link](buffer); // Lire manuellement
5. // Traiter la requête
6. OutputStream out = [Link]();
7. [Link](reponse); // Envoyer manuellement
8. [Link]();
🔴 PROBLÈME AVEC LES SOCKETS
❌ Tu dois MANUELLEMENT créer les messages
❌ Tu dois MANUELLEMENT gérer les connexions
❌ Tu dois MANUELLEMENT lire/écrire les données
❌ Tu dois MANUELLEMENT gérer les erreurs réseau
❌ Beaucoup de CODE RÉPÉTITIF (sockets, streams, buffers...)
💡 IDÉE GÉNIALE : Et si on pouvait juste APPELER UNE FONCTION sur le serveur comme si
c'était une fonction locale ? Sans gérer tout ce bazar de sockets ?
PARTIE 2 : RPC - L'IDÉE RÉVOLUTIONNAIRE
🚀 Qu'est-ce que le RPC ?
RPC = Remote Procedure Call = Appel de Procédure Distante
DÉFINITION SIMPLE :
RPC est un mécanisme qui permet d'appeler une fonction qui s'exécute sur une AUTRE
machine, exactement comme si tu appelais une fonction LOCALE.
📖 ANALOGIE : Le restaurant avec service de livraison
SANS RPC (avec sockets) :
Imagine que tu veux manger une pizza :
1. Tu CONSTRUIS toi-même un message : "Je veux une pizza margherita"
2. Tu TROUVES l'adresse du restaurant
3. Tu ENVOIES la lettre par la poste
4. Tu ATTENDS la réponse
5. Tu LIS la réponse
6. Tu PAIES et RÉCUPÈRES la pizza
→ C'EST COMPLIQUÉ ! Tu gères TOUT toi-même.
AVEC RPC :
Tu appelles juste : commander_pizza("margherita")
→ MAGIQUEMENT, la pizza arrive !
→ Tout le reste (message, envoi, attente, etc.) est AUTOMATIQUE !
C'EST ÇA LE RPC !
🔧 COMMENT ÇA MARCHE CONCRÈTEMENT ?
Le RPC utilise deux composants MAGIQUES appelés STUBS :
1. Le STUB CLIENT (côté client)
Rôle : Faire CROIRE au client que la fonction est locale
Le stub client est une FAUSSE fonction qui a la même signature que la vraie
Quand tu l'appelles, il :
→ Transforme tes paramètres en MESSAGE
→ ENVOIE le message au serveur
→ ATTEND la réponse
→ Transforme la réponse en valeur de retour
→ Tu as l'ILLUSION d'un appel local !
2. Le STUB SERVEUR / SKELETON (côté serveur)
Rôle : Transformer les messages en vrais appels de fonction
Le skeleton ÉCOUTE les messages qui arrivent
Quand un message arrive, il :
→ LIT le message
→ Extrait les paramètres
→ APPELLE la VRAIE fonction sur le serveur
→ Récupère le résultat
→ Transforme le résultat en MESSAGE
→ RENVOIE le message au client
📊 SCHÉMA DU FONCTIONNEMENT RPC
┌─────────────────────────┐ ┌─────────────────────────┐
│ CLIENT │ │ SERVEUR │
│ │ │ │
│ ton_code() │ │ vraie_fonction() │
│ ↓ │ │ ↑ │
│ appel fonction() ──────┼────┐ │ │ │
│ ↓ │ │ │ │ │
│ ┌─────────────┐ │ │ │ ┌────────────┐ │
│ │ STUB CLIENT │ │ │ │ │ SKELETON │ │
│ │ (fausse │ │ │ │ │ (serveur) │ │
│ │ fonction) │ │ │ │ │ │ │
│ └─────────────┘ │ │ │ └────────────┘ │
│ ↓ │ │ │ ↑ │
│ crée MESSAGE │ │ │ reçoit MESSAGE │
│ ↓ │ │ │ ↓ │
└───────│─────────────────┘ │ └───────│─────────────────┘
│ │ │
│ MESSAGE RÉSEAU │ │
│ (via sockets) │ │
└──────────────────────┼────────────┘
│
↓ ↓ ↑
Requête Réseau Réponse
🎬 EXEMPLE PAS À PAS
Imaginons que tu veux appeler une fonction `dire_bonjour(nom)` sur le serveur :
ÉTAPE 1 - CLIENT :
ton_code.java :
resultat = dire_bonjour("Harisson");
↓ Le client appelle le STUB CLIENT
ÉTAPE 2 - STUB CLIENT :
Point A :
- Collecte le paramètre : "Harisson"
- Crée un message : { fonction: "dire_bonjour", params: ["Harisson"] }
- Génère un ID : 12345
- Lance un TIMER (pour détecter les pertes)
- ENVOIE le message au serveur
ÉTAPE 3 - RÉSEAU :
Le message voyage sur le réseau (via sockets TCP/UDP)
ÉTAPE 4 - SKELETON (serveur) :
Point B :
- REÇOIT le message
- LIT : fonction="dire_bonjour", params=["Harisson"]
- Enregistre l'ID 12345 (pour éviter les doublons)
Point C :
- APPELLE la vraie fonction :
String resultat = dire_bonjour("Harisson");
- Obtient : "Bonjour Harisson !"
Point D :
- Crée un message de réponse : { id: 12345, resultat: "Bonjour Harisson !" }
- ENVOIE le message au client
ÉTAPE 5 - STUB CLIENT :
Point E :
- REÇOIT la réponse
- LIT le résultat : "Bonjour Harisson !"
- ARRÊTE le timer
- Envoie un ACCUSÉ DE RÉCEPTION
- RETOURNE le résultat à ton code
ÉTAPE 6 - TON CODE :
resultat = "Bonjour Harisson !"
→ Tu as l'impression d'avoir appelé une fonction locale !
GESTION DES ERREURS RÉSEAU
Le RPC gère AUTOMATIQUEMENT les problèmes réseau :
Problème 1 : Le message de requête est perdu
→ Le TIMER du client expire
→ Le stub client ré-envoie le message (avec le MÊME ID)
→ Abandon après N tentatives
Problème 2 : Le message de réponse est perdu
→ Le TIMER du serveur expire
→ Le skeleton ré-envoie la réponse
→ OU le client reçoit une réponse en double (même ID) et ré-envoie l'accusé
Problème 3 : Le serveur reçoit la même requête 2 fois
→ Le skeleton vérifie l'ID
→ ID déjà vu ? → Ne ré-exécute PAS la fonction, renvoie juste la réponse
✅ RÉSULTAT : Tu n'as RIEN à gérer ! Le RPC s'occupe de TOUT !
PARTIE 3 : JAVA RMI - RPC ORIENTÉ OBJET
🎯 Qu'est-ce que Java RMI ?
RMI = Remote Method Invocation = Invocation de Méthode Distante
RMI = RPC pour Java, mais avec des OBJETS !
RPC classique : Appelle des FONCTIONS distantes
Java RMI : Appelle des MÉTHODES sur des OBJETS distants
🔑 DIFFÉRENCE FONDAMENTALE
RPC :
appeler_fonction(param1, param2)
→ Appelle une fonction sur un serveur
RMI :
objet_distant.methode(param1, param2)
→ Appelle une MÉTHODE sur un OBJET qui est sur un serveur
ARCHITECTURE JAVA RMI
RMI utilise 3 composants principaux :
1. Le STUB (côté client)
C'est un FAUX OBJET qui implémente la même interface que l'objet serveur.
Quand tu appelles une méthode dessus, il transforme l'appel en message réseau.
2. Le SKELETON (côté serveur)
C'est un objet qui écoute les messages réseau.
Quand il reçoit un message, il appelle la vraie méthode sur le vrai objet serveur.
3. Le RMIREGISTRY (service de nommage)
C'est un ANNUAIRE qui permet de retrouver les objets distants par leur nom.
Comme un bottin téléphonique : tu donnes un nom, il te donne une référence.
📝 COMMENT PROGRAMMER EN RMI ? (4 ÉTAPES)
ÉTAPE 1 : Définir l'INTERFACE
// [Link]
import [Link];
import [Link];
public interface Hello extends Remote {
public String dire_bonjour(String nom) throws RemoteException;
}
RÈGLES IMPORTANTES :
✓ L'interface doit extends Remote
✓ Chaque méthode doit throws RemoteException
ÉTAPE 2 : Implémenter le SERVEUR
// [Link]
import [Link].*;
import [Link];
public class HelloImpl extends UnicastRemoteObject implements Hello {
// Le constructeur DOIT throws RemoteException
public HelloImpl() throws RemoteException {
super();
}
// La vraie méthode
public String dire_bonjour(String nom) throws RemoteException {
return "Bonjour " + nom + " !";
}
public static void main(String[] args) {
try {
// 1. Créer l'objet serveur
Hello obj = new HelloImpl();
// 2. L'enregistrer dans le rmiregistry
[Link]("//localhost/MonServeur", obj);
[Link]("Serveur prêt !");
} catch (Exception e) {
[Link]();
}
}
}
RÈGLES IMPORTANTES :
✓ La classe extends UnicastRemoteObject
✓ Elle implements l'interface Hello
✓ On enregistre l'objet dans le rmiregistry avec [Link]()
ÉTAPE 3 : Programmer le CLIENT
// [Link]
import [Link].*;
public class HelloClient {
public static void main(String[] args) {
try {
// 1. Obtenir le stub depuis le rmiregistry
Hello obj = (Hello) [Link]("//localhost/MonServeur");
// 2. Appeler la méthode comme si l'objet était local !
String resultat = obj.dire_bonjour("Harisson");
// 3. Afficher le résultat
[Link]("Réponse du serveur : " + resultat);
} catch (Exception e) {
[Link]();
}
}
}
CE QUI SE PASSE :
1. [Link]() contacte le rmiregistry
2. Le rmiregistry renvoie un STUB (faux objet)
3. Quand tu appelles obj.dire_bonjour(), c'est le STUB qui est appelé
4. Le stub envoie un message au serveur
5. Le serveur exécute la vraie méthode
6. Le résultat revient au client
7. Tu as l'impression d'un appel local !
ÉTAPE 4 : COMPILER et EXÉCUTER
# 1. Compiler
javac [Link]
javac [Link]
javac [Link]
# 2. Lancer le rmiregistry (dans un terminal)
rmiregistry &
# 3. Lancer le serveur (dans un autre terminal)
java HelloImpl
→ Affiche : "Serveur prêt !"
# 4. Lancer le client (dans un 3ème terminal)
java HelloClient
→ Affiche : "Réponse du serveur : Bonjour Harisson !"
PARTIE 4 : PASSAGE DE PARAMÈTRES (IMPORTANT !)
Quand tu passes des paramètres à une méthode RMI, il y a 2 cas TRÈS différents :
CAS 1 : Paramètre SERIALIZABLE (copie)
Serializable = L'objet est COPIÉ vers le serveur (clonage)
interface Personne extends Serializable {
String getNom();
}
// CLIENT :
Personne p = new PersonneImpl("Harisson");
[Link](p); // p est COPIÉ sur le serveur
// SERVEUR :
public void traiter(Personne p) {
// Ici, p est un CLONE de l'objet client
// Si je modifie p, ça n'affecte PAS l'objet client !
}
CAS 2 : Paramètre REMOTE (référence distante)
Remote = Le STUB est copié, donc tu gardes une référence à l'objet original
interface Calculatrice extends Remote {
int additionner(int a, int b) throws RemoteException;
}
// CLIENT :
Calculatrice calc = ... // objet distant
[Link](calc); // Le STUB de calc est copié
// SERVEUR :
public void utiliser(Calculatrice calc) {
// Ici, calc est un STUB
// Si j'appelle [Link](), ça fait un appel RMI
// vers l'objet original sur le client !
}
PARTIE 5 : SOCKETS vs RMI - COMPARAISON FINALE
╔════════════════════════════════════════════════════════════════╗
║ SOCKETS vs RMI ║
╚════════════════════════════════════════════════════════════════╝
┌──────────────────────┬───────────────────┬────────────────────┐
│ CRITÈRE │ SOCKETS │ RMI │
├──────────────────────┼───────────────────┼────────────────────┤
│ Complexité │ ÉLEVÉE │ FAIBLE │
│ │ Beaucoup de code │ Peu de code │
├──────────────────────┼───────────────────┼────────────────────┤
│ Gestion messages │ MANUELLE │ AUTOMATIQUE │
│ │ Tu codes tout │ Transparent │
├──────────────────────┼───────────────────┼────────────────────┤
│ Gestion erreurs │ MANUELLE │ AUTOMATIQUE │
│ │ Tu gères timeout │ Géré par RMI │
├──────────────────────┼───────────────────┼────────────────────┤
│ Syntaxe │ send/receive │ appel méthode │
│ │ │ (comme local) │
├──────────────────────┼───────────────────┼────────────────────┤
│ Contrôle │ TOTAL │ LIMITÉ │
│ │ Tu contrôles tout │ RMI décide │
├──────────────────────┼───────────────────┼────────────────────┤
│ Performance │ MEILLEURE │ BONNE │
│ │ Direct, optimisé │ Overhead RMI │
├──────────────────────┼───────────────────┼────────────────────┤
│ Langage │ MULTI-LANGAGE │ JAVA SEULEMENT │
│ │ C, Java, Python │ │
├──────────────────────┼───────────────────┼────────────────────┤
│ Cas d'usage │ Protocoles bas │ Applications │
│ │ niveau, perfs │ distribuées Java │
└──────────────────────┴───────────────────┴────────────────────┘
QUAND UTILISER SOCKETS ?
✓ Tu as besoin de CONTRÔLE total
✓ Tu as besoin de PERFORMANCE maximale
✓ Tu communiques avec d'AUTRES LANGAGES
✓ Tu codes un PROTOCOLE réseau (HTTP, FTP, etc.)
QUAND UTILISER RMI ?
✓ Tu veux du code SIMPLE et RAPIDE à écrire
✓ Ton application est 100% JAVA
✓ Tu veux de la communication TRANSPARENTE
✓ Tu construis une application DISTRIBUÉE
PARTIE 6 : CONSEILS PRATIQUES POUR TON STAGE
🎓 Ce que tu dois ABSOLUMENT retenir
✓ RMI = Appel de méthode distant qui RESSEMBLE à un appel local
✓ STUB client = Faux objet qui transforme l'appel en message
✓ SKELETON serveur = Objet qui transforme le message en appel réel
✓ rmiregistry = Annuaire pour trouver les objets distants
✓ extends Remote + throws RemoteException = OBLIGATOIRE
✓ Serializable = COPIE de l'objet
✓ Remote = RÉFÉRENCE à l'objet
💡 Trucs et astuces
💡 Lance TOUJOURS le rmiregistry AVANT le serveur
💡 Si ça ne marche pas, vérifie que le rmiregistry tourne (ps aux | grep rmiregistry)
💡 Les ports par défaut : rmiregistry=1099, tes objets=ports aléatoires
💡 Pour déboguer, lance le rmiregistry dans la JVM du serveur
([Link]())
💡 N'oublie JAMAIS le throws RemoteException dans l'interface
💡 Si tu as une erreur "stub not found", c'est souvent un problème de classpath
🔥 Erreurs courantes à éviter
❌ Oublier extends Remote dans l'interface → Erreur de compilation
❌ Oublier throws RemoteException → Erreur de compilation
❌ Ne pas lancer le rmiregistry → [Link]
❌ Mauvais URL dans [Link]() → NotBoundException
❌ Oublier extends UnicastRemoteObject → Le stub n'est pas créé
PARTIE 7 : EXERCICES POUR T'ENTRAÎNER
Exercice 1 : Calculatrice distante (Niveau 1)
Crée un service RMI qui propose 4 méthodes : add(), sub(), mul(), div()
Le client envoie deux nombres et l'opération, le serveur retourne le résultat.
Exercice 2 : Compteur partagé (Niveau 2)
Crée un compteur distant avec increment(), decrement(), getValue()
Lance plusieurs clients qui incrémentent/décrémentent en même temps.
Observe la synchronisation !
Exercice 3 : Chat distribué (Niveau 3)
Crée un serveur de chat avec RMI.
Les clients s'enregistrent avec register(nom, callback)
Quand un client envoie un message, le serveur le broadcast à tous.
Utilise un objet Remote comme callback !
Exercice 4 : Système de fichiers distant (Niveau 4)
Crée un serveur qui expose : listFiles(), readFile(), writeFile()
Le client peut manipuler des fichiers sur le serveur à distance.
Bonus : ajoute la gestion des permissions !
CONCLUSION : TON PARCOURS D'APPRENTISSAGE
🎉 FÉLICITATIONS HARISSON !
TON PARCOURS :
Étape 1 : SOCKETS ✓
→ Tu as appris à faire communiquer 2 programmes
→ LoadBalancer fonctionnel
→ Tu comprends le réseau au niveau BAS
Étape 2 : RPC/RMI ← Tu es ici !
→ Tu apprends à SIMPLIFIER la communication
→ Appels de méthodes distantes
→ Tu comprends l'abstraction
Étape 3 : Prochaines étapes
→ Web Services (SOAP, REST)
→ Microservices
→ Message Queues (RabbitMQ, Kafka)
→ gRPC
→ Systèmes distribués avancés
💪 MESSAGE FINAL :
RMI est la BASE pour comprendre TOUTES les technologies distribuées modernes. Les APIs
REST, les microservices, gRPC... tout ça repose sur les MÊMES principes que RMI !
Maintenant que tu maîtrises les sockets ET le RMI, tu as les fondations SOLIDES pour
devenir un excellent ingénieur en systèmes distribués.
CONTINUE COMME ÇA ! 🚀🇨🇲
═════════════════════════════════════════════════════════════════
═════
FIN DE L'EXPLICATION