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