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
multiplyque multipliqui dos nombres. - Implementa el servei en un servidor RPC.
- Escriu un client que cridi el procediment
multiplyi mostri el resultat.
Exercici 2: Implementar un Servei RMI
- Defineix una interfície per a un servei que tingui un mètode
concatenateque concateni dues cadenes de text. - Implementa el servei en un servidor RMI.
- Escriu un client que cridi el mètode
concatenatei 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
