La Chat Client-Server - Dipartimento di Informatica

Transcript

La Chat Client-Server - Dipartimento di Informatica
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Organizzazione della lezione
Lezione 18
Remote Method Invocation - 6
Vittorio Scarano
Corso di Programmazione Distribuita (2003-2004)
Laurea di I livello in Informatica
Università degli Studi di Salerno
• Il problema dell’accesso al registry per il rebind()
• Una soluzione con registry multipli
• Alcuni commenti finali
2
Una chat client-server (con callback)
• Il problema:
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
L’accesso al registry per il rebind()
– su un registry che si trova su un host diverso dalla JVM
• si può fare lookup() e list()
• ma non si può fare la bind(), rebind() !
• Esaminiamo le architetture delle applicazioni chat
– Chat client-server (con call-back)
– Chat peer-to-peer
3
• Il server
– riceve da ogni client la iscrizione/abbandono della chat
– manda a tutti i client quello che ognuno dice (broadcast)
• Meccanismo mediante il quale il client chiede al server
di essere richiamato (call-back)
– con RMI il client (dotato di interface remota) passa il
proprio riferimento remoto al server che lo usa per
contattarlo successivamente
– necessità del call-back implicita nella applicazione:
– il server deve poter informare ogni client di quanto digitato da un client
– permette ad ogni utente di avere la informazione su chi è iscritto alla chat
4
Le classi di Chat Client-Server
Server.java
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
import java.rmi.*;
public interface Server
extends java.rmi.Remote {
public void dico (Messaggio m)
throws RemoteException;
public void iscrivi (Remote idRef)
throws RemoteException;
public void abbandona (Remote idRef)
throws RemoteException;
}
• Tre metodi remoti
– dico()
• chiamato da un client se
deve scrivere a tutti
– iscrivi()
• iscrizione alla chat
– abbandona()
• per abbandonare
• Passaggio del
riferimento remoto
necessario per il callback
5
Le classi di Chat Client-Server
6
Chat.java
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
import java.rmi.*;
7
public interface Chat
extends java.rmi.Remote {
public void dico (Messaggio m)
throws RemoteException;
public void iscrivi (String nickIscritto)
throws RemoteException;
public void abbandona (String nickAbbandona)
throws RemoteException;
public String getNickname ()
throws RemoteException;
}
• Interfaccia remota
• Quattro metodi remoti
– dico()
• chiamato dal server per il
broadcast di messaggi
– iscrivi(), abbandona()
• consapevolezza della
iscrizione/abbandono alla
chat di un utente
– getNickname()
• utilità per uso da parte del
server (non fondamentale)
• Metodi chiamati solo dal
server
8
La Chat Client-Server: architettura (2)
• Set-up del server
• Iscrizione di un client ClientUno
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
La Chat Client-Server: architettura (1)
rmiregistry
lookup()
Server
rebind()
ServerChat
ClientUno
iscrivi()
host2
ClientUno
host1
• Iscrizione di un client ClientDue
– notare che non servono registry per arrivare a ClientUno e
ClientDue .. infatti…
lookup()
rmiregistry
ClientDue
RemRef
host2
iscrivi()
ServerChat
ClientUno
ClientUno
ClientDue
host2
host1
9
Commenti sulla architettura della Chat
• Per il call-back il server usa i riferimenti remoti che ha
– e che sono stati comunicati con LVFULYL
rmiregistry
ClientDue
dico(“Ciao”)
RemRef
host2
ServerChat
ClientUno
dico(“Ciao”)
ClientUno
dico(“Ciao”)
ClientDue
host2
host1
11
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
La Chat Client-Server: architettura (3)
10
• La architettura Client-Server della Chat
– offre un singolo punto di “debolezza” (il server)
– non risponde alla struttura del problema
• in effetti, ogni client deve poter inviare a ciascun altro client
• serve solamente poter sapere chi sono i client registrati
• Possibile passare ad una architettura Peer-to-peer
– dove l’unica componente centralizzata è il registry
• che deve necessariamente essere presente (comunque) anche sulla
architettura client-server
12
Le classi di Chat P2P
• Per ogni applicazioni peer-to-peer è comunque sempre
necessario una componente che permetta il bootstrap:
– “locazione” fissata per poter contattare gli altri peer
• Nel caso di RMI si può usare il registry
– tramite il metodo list() è possibile sapere tutti gli id
(stringhe) degli oggetti remoti registrati
– a partire da cui si può ottenere il riferimento remoto
– che può essere usato per contattare gli oggetti
(discriminando, se è il caso, rispetto alle interface
implementate)
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Il ruolo del registry nelle applicazioni P2P
• se sul registry stanno in esecuzione diverse applicazioni distribuite13
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
• Interfaccia remota
• Quattro metodi remoti
– dico()
• chiamato da un altro peer per
scrivere un messaggio
– iscrivi(), abbandona()
• inserimento o cancellazione
nella lista dei peers che
ognuno ha
– getNickname()
• utilità per uso da parte degli
altri peers (non
fondamentale)
15
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Alcuni commenti sulla Chat
Chat.java
import java.rmi.*;
public interface Chat
extends java.rmi.Remote {
public void dico (Messaggio m)
throws RemoteException;
public void iscrivi (String id)
throws RemoteException;
public void abbandona (Remote idRef)
throws RemoteException;
public String getNickname ()
throws RemoteException;
}
14
• Applicazione peer-to-peer
– peer di “dimensione” maggiore rispetto al client della
soluzione client-server
– non soggetta a fault-locali
• in caso il registry cada, chi è connesso continua a chattare fino alla
chiusura (partial faults tolerance)
• Un problema:
– su un registry che si trova su un host diverso dalla JVM
• si può fare lookup() e list()
• ma non si può fare la bind(), rebind() !
– con questa implementazione si può fare una chat solo se ci si
16
trova sullo stesso host (stesso registry!)
La Chat P2P (2) sullo stesso host
• Iscrizione di un peer PeerUno
• Iscrizione di un peer PeerDue:
ವ ORRNXS
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Il problema della Chat P2P su host diversi
al registry ok, LVFULYL a ClientUno ok, UHELQG su registry fallisce!
rmiregistry
lookup()
rebind()
PeerUno
rebind()
PeerUno
PeerDue
iscrivi()
host2
PeerDue
host1
• Iscrizione di un peer PeerUno
• Iscrizione di un peer PeerDue:
ವ ORRNXS
al registry ok, LVFULYL a ClientUno ok, UHELQG ok!
rmiregistry
lookup()
rebind()
PeerDue PeerUno
rebind()
PeerUno
PeerDue
iscrivi()
PeerDue
host1
17
Una soluzione: ChatMultipleRegistry
• Il problema dell’accesso al registry per il rebind()
• Una soluzione con registry multipli
• Alcuni commenti finali
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Organizzazione della lezione
18
19
• L’idea:
– ogni peer ha una lista di host su cui si potrebbero trovare dei
peer che partecipano alla chat
– per ogni host nella lista
• controlla se esiste un registry
• se esiste il registry, preleva la lista dei nomi
• chiede i riferimenti remoti
– si registra sul proprio registry
• Da notare: ogni registry può servire più oggetti Chat
20
ChatImpl.java: main() (1)
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Le classi di ChatMultipleRegistry (identiche)
import java.io.*;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;
public class ChatImpl
extends UnicastRemoteObject
implements Chat {
• Identico al precedente
public static void main(String args[]) {
if (args.length > 0 ) nickname = args[0];
else {
System.out.println (“Serve nickname");
System.exit(1);
}
System.setSecurityManager(
new RMISecurityManager());
// continua…
21
try {
ArrayList nomi = new ArrayList();
String n[] ;
for (int i = 0; i < HOST.length; i++) {
n = null;
try {
n = Naming.list("rmi://"+HOST[i]);
} catch (ConnectException e) {
System.out.println(HOST[i]+
" non presente..");
}
if (n != null)
for (int j=0; j < n.length; j++)
nomi.add(n[j]);
}
System.out.println ("Risultano connessi "+
nomi.size()+" utenti:");
for (int i=0 ; i < nomi.size(); i++)
System.out.println ("\t"+nomi.get(i));
// continua…
ChatImpl.java: main() (3)
• Ottiene l’elenco dei
nomi da tutti i registry
presenti
– preleva la lista da ogni
registry
• se genera una eccezione
continua
– se n[] riferisce ad un
array: esiste il registry..
• e allora si inseriscono i
nomi nell’array di nomi[]
• Stampa le informazioni
23
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
ChatImpl.java: main() (2)
22
for (int i=0 ; i < nomi.size(); i++)
peers.add(Naming.lookup (
(String) nomi.get(i)));
ChatImpl myself = new ChatImpl();
Naming.rebind(nickname, myself);
• Riferimenti remoti
– ogni nome prelevato dal
registry contiene le
informazione sul registry
• rmi://10.0.0.24:1099/Pippo
System.out.println (“Iscrizione ai peers:");
for (int i=0 ; i < peers.size(); i++) {
System.out.print ("\tIscrizione a "+((Chat)
peers.get (i)).getNickname()+"..");
((Chat) peers.get (i)).iscrivi (nickname) ;
System.out.println ("effettuata!");
}
} catch (Exception e) {
System.out.println("Eccezione");
e.printStackTrace();
}
// resto del main (Shell) identico a Chat P2P
• Registrazione sul proprio
registry
– solo con il nickname usa
localhost
• Iscrizione presso gli altri
peer:
– chiamata di iscrivi()
24
ChatImpl.java: ultime differenze
ChatImpl.java: iscrivi() (unico altra differenza)
}
• Il metodo remoto iscrivi()
– riceve una id (stringa)
– e adesso deve cercare su
tutti i registry!
• Cerca la id sugli host
– ignora eccezioni da qui:
• o niente registry sull’host
• o niente id sul registry
• Se la trovo
• aggiungo alla lista dei peer
il riferimento remoto
• esco dal for
• Stampo il prompt
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
public void iscrivi (String id)
throws RemoteException {
try {String n[];
Remote ref;
for (int i = 0; i < HOST.length; i++) {
ref = null;
try { ref = Naming.lookup(
"rmi://"+HOST[i]+"/"+id);
} catch (RemoteException e) { }
if (ref != null) {// trovato!
peers.add(Naming.lookup (
"rmi://"+HOST[i]+"/"+id));
break; //esce dal for
}
} //end for
} catch (Exception e) {
System.out.println ("Id non presente?");
e.printStackTrace(); }
System.out.print ("\nEntra "+id+"\n"+PROMPT);
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
lookup()
rmiregistry
PeerUno
PeerDue
lookup()
lookup()
rebind()
lookup()
PeerTre
PeerDue
PeerUno
PeerUno
PeerDue
PeerTre
rebind()
rebind()
lookup()
PeerUno
iscrivi()
PeerDue
PeerTre
iscrivi()
iscrivi()
host2
} //fine classe ChatImpl
• Attenzione:
– il localhost deve essere
incluso all’interno della
lista di indirizzi
• Commenti:
– la lista di host può essere
prelevata da un server
• il peer diventa client (solo
per la lista) in una
architettura client-server
– si può utilizzare un
meccanismo di caching
Organizzazione della lezione
– PeerUno, PeerDue e PeerTre
PeerTre
public static final String[ ] HOST = {
"10.0.0.99",
"10.0.0.1",
"10.0.0.137",
"10.0.0.201“ };
– qui è un array di stringhe
26
• Iscrizione di peers su due host
lookup()
//Definizione dell’elenco di host
• La costante HOST
25
Chat P2P con Multiple Registry
rmiregistry
//Altri metodi e variabili identici a ChatImpl P2P
host1
27
• Il problema dell’accesso al registry per il rebind()
• Una soluzione con registry multipli
• Alcuni commenti finali
28
La “race condition” (1)
• La situazione come dovrebbe essere…
• Un commento generale:
– qualsiasi sistema peer-to-peer deve dipendere da qualche
“entità” esterna (server?) per il bootstrap
• Le “scarne” soluzioni proposte sono a scopo didattico:
– tra le altre cose, non coprono:
• nickname duplicati
– sovrascrivono “senza pietà” riferimenti se più utenti hanno la stessa
• situazioni di malfunzionamenti parziali (catch da completare)
– caduta di un registry durante la chat
• problemi di “race condition”
– due client che si connettono contemporaneamente allo stesso registry ma le
cui operazioni di iscrivi() e rebind sono “mischiate” (interleaved)
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Commenti sulle architetture proposte (1)
– funziona tutto…
rmiregistry
rmiregistry
lookup()
PeerUno
PeerDue
lookup()
lookup()
rebind()
rebind()
PeerUno
PeerDue
iscrivi()
PeerUno
lookup()
PeerDue
host2
host1
29
La “race condition” (2)
30
Una “soluzione” alla “race condition”
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
• La situazione come potrebbe essere…
– con la “legge di Murphy”
• “se una cosa può andare storta, lo farà!”
rmiregistry
rmiregistry
lookup()
PeerUno
PeerDue
lookup()
lookup()
rebind()
PeerDue
host2
rebind()
lookup()
PeerUno
host1
31
• Prevedere di fare il UHELQG prima della “iscrizione” a
tutti i nodi reperiti:
– in questa maniera, però, è necessario:
• le lookup() devono avvenire nello stesso ordine: prima l’host locale
poi tutti gli altri
• controllare che le invocazioni di LVFULYL che arrivano a un nodo
non siano di nodi che già abbiamo nel nostro array di peers
• controllare di non effettuare alcuna chiamata remota a sé stessi
– controllando che il riferimento di cui si vuole invocare il metodo remoto
(chiamando il metodo LVFULYL o il metodo DEEDQGRQD) sia diverso dal
proprio riferimento
– oppure controllando che il riferimento da inserire nell’arraylist peers sia
diverso dal proprio
32
La “race condition” (3): Soluzione
• Si fa il UHELQG prima di cercare altri nodi
• Si fa il rebind() prima di cercare altri nodi
• Funzionamento senza problemi di “race condition”
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Programmazione Distribuita (2003-2004). Vi.ttorio Scarano
Come funziona questa soluzione
– i riferimenti in blu sono auto-riferimenti ed è necessario non effettuare chiamate
remote (abbandona(), iscrivi(), dico() etc.) oppure non inserirlo nell’array peers
rmiregistry
rmiregistry
lookup()
PeerUno
PeerDue
lookup()
lookup()
rebind()
rebind()
PeerUno
PeerDue
iscrivi()
PeerUno
lookup()
PeerUno
PeerDue
PeerDue
host2
host1
33
•
Un ulteriore controllo da effettuare:
•
ricezione di LVFULYL da nodi già in peers
rmiregistry
rmiregistry
lookup()
PeerUno
PeerDue
lookup()
lookup()
rebind()
rebind()
PeerUno
PeerDue
iscrivi()
PeerUno
lookup()
No. Già
presente!
PeerUno
PeerDue
PeerDue
host2
host1
34