Introducció
En aquest tema, explorarem dos mecanismes fonamentals per a la comunicació en sistemes distribuïts: Remote Procedure Call (RPC) i Remote Method Invocation (RMI). Aquests mecanismes permeten que els programes executats en diferents màquines puguin comunicar-se i col·laborar de manera eficient.
Objectius
- Entendre els conceptes bàsics de RPC i RMI.
- Aprendre com funcionen i com es poden implementar.
- Comparar RPC i RMI per identificar les seves diferències i similituds.
- Proporcionar exemples pràctics i exercicis per reforçar els conceptes apresos.
Conceptes Bàsics
Remote Procedure Call (RPC)
RPC és un protocol que permet a un programa executar una subrutina (procediment) en una altra adreça espacial (normalment en una màquina remota) com si fos una crida local.
Característiques de RPC
- Transparència: L'objectiu de RPC és fer que la crida a un procediment remot sembli una crida local.
- Interfície: Utilitza una interfície definida per a especificar les funcions que es poden cridar remotament.
- Serialització: Les dades es serialitzen (marshalling) per enviar-les a través de la xarxa i es deserialitzen (unmarshalling) en el costat receptor.
Remote Method Invocation (RMI)
RMI és un mecanisme similar a RPC, però específic per a Java, que permet a un objecte invocar mètodes en un altre objecte situat en una màquina remota.
Característiques de RMI
- Orientat a Objectes: A diferència de RPC, RMI treballa amb objectes i permet cridar mètodes en objectes remots.
- Serialització d'Objectes: RMI utilitza la serialització d'objectes Java per enviar dades a través de la xarxa.
- Registre de Serveis: RMI proporciona un registre de serveis (RMI registry) on els objectes remots es poden registrar i localitzar.
Funcionament de RPC
Passos per a una Crida RPC
- Definició de la Interfície: Es defineix una interfície que especifica els procediments que es poden cridar remotament.
- Implementació del Servei: Es crea una implementació del servei que defineix el comportament dels procediments remots.
- Stub i Skeleton:
- Stub: Actua com a representant del client i s'encarrega de la serialització de les dades.
- Skeleton: Actua com a representant del servidor i s'encarrega de deserialitzar les dades i cridar el procediment adequat.
- Comunicació: El stub envia la crida al skeleton a través de la xarxa, i el skeleton retorna el resultat al stub.
Exemple de RPC en Python
# rpc_server.py import xmlrpc.server def add(x, y): return x + y server = xmlrpc.server.SimpleXMLRPCServer(("localhost", 8000)) print("Listening on port 8000...") server.register_function(add, "add") server.serve_forever()
# rpc_client.py import xmlrpc.client proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") result = proxy.add(5, 3) print("Result:", result)
Funcionament de RMI
Passos per a una Crida RMI
- Definició de la Interfície: Es defineix una interfície que especifica els mètodes que es poden cridar remotament.
- Implementació del Servei: Es crea una implementació del servei que defineix el comportament dels mètodes remots.
- Registre de Serveis: El servei es registra en el RMI registry perquè els clients el puguin localitzar.
- Comunicació: El client utilitza el RMI registry per obtenir una referència a l'objecte remot i cridar els seus mètodes.
Exemple de RMI en Java
// RMIInterface.java import java.rmi.Remote; import java.rmi.RemoteException; public interface RMIInterface extends Remote { int add(int x, int y) throws RemoteException; }
// RMIServer.java import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer implements RMIInterface { public int add(int x, int y) { return x + y; } public static void main(String[] args) { try { RMIServer obj = new RMIServer(); RMIInterface stub = (RMIInterface) UnicastRemoteObject.exportObject(obj, 0); Registry registry = LocateRegistry.createRegistry(1099); registry.bind("RMIInterface", stub); System.out.println("Server ready"); } catch (Exception e) { e.printStackTrace(); } } }
// RMIClient.java import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIClient { public static void main(String[] args) { try { Registry registry = LocateRegistry.getRegistry("localhost"); RMIInterface stub = (RMIInterface) registry.lookup("RMIInterface"); int result = stub.add(5, 3); System.out.println("Result: " + result); } catch (Exception e) { e.printStackTrace(); } } }
Comparació entre RPC i RMI
Característica | RPC | RMI |
---|---|---|
Paradigma | Procedural | Orientat a Objectes |
Llenguatge | Multi-llenguatge | Java |
Serialització | Dades primitives | Objectes Java |
Registre de Serveis | No | Sí (RMI registry) |
Facilitat d'ús | Simple | Més complex |
Exercicis Pràctics
Exercici 1: Implementar un Servei RPC
- Defineix una interfície per a un servei que tingui un procediment
multiply
que multipliqui dos nombres. - Implementa el servei en un servidor RPC.
- Escriu un client que cridi el procediment
multiply
i mostri el resultat.
Exercici 2: Implementar un Servei RMI
- Defineix una interfície per a un servei que tingui un mètode
concatenate
que concateni dues cadenes de text. - Implementa el servei en un servidor RMI.
- Escriu un client que cridi el mètode
concatenate
i mostri el resultat.
Solucions
Solució Exercici 1
# rpc_server.py import xmlrpc.server def multiply(x, y): return x * y server = xmlrpc.server.SimpleXMLRPCServer(("localhost", 8000)) print("Listening on port 8000...") server.register_function(multiply, "multiply") server.serve_forever()
# rpc_client.py import xmlrpc.client proxy = xmlrpc.client.ServerProxy("http://localhost:8000/") result = proxy.multiply(5, 3) print("Result:", result)
Solució Exercici 2
// RMIInterface.java import java.rmi.Remote; import java.rmi.RemoteException; public interface RMIInterface extends Remote { String concatenate(String a, String b) throws RemoteException; }
// RMIServer.java import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; public class RMIServer implements RMIInterface { public String concatenate(String a, String b) { return a + b; } public static void main(String[] args) { try { RMIServer obj = new RMIServer(); RMIInterface stub = (RMIInterface) UnicastRemoteObject.exportObject(obj, 0); Registry registry = LocateRegistry.createRegistry(1099); registry.bind("RMIInterface", stub); System.out.println("Server ready"); } catch (Exception e) { e.printStackTrace(); } } }
// RMIClient.java import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; public class RMIClient { public static void main(String[] args) { try { Registry registry = LocateRegistry.getRegistry("localhost"); RMIInterface stub = (RMIInterface) registry.lookup("RMIInterface"); String result = stub.concatenate("Hello, ", "World!"); System.out.println("Result: " + result); } catch (Exception e) { e.printStackTrace(); } } }
Resum
En aquest tema, hem après sobre RPC i RMI, dos mecanismes essencials per a la comunicació en sistemes distribuïts. Hem explorat les seves característiques, el seu funcionament i hem vist exemples pràctics de com implementar-los. També hem comparat RPC i RMI per entendre millor les seves diferències i similituds. Finalment, hem proporcionat exercicis pràctics per reforçar els conceptes apresos.
Curs d'Arquitectures Distribuïdes
Mòdul 1: Introducció als Sistemes Distribuïts
- Conceptes Bàsics de Sistemes Distribuïts
- Models de Sistemes Distribuïts
- Avantatges i Desafiaments dels Sistemes Distribuïts