Prova pratica di Sistemi Distribuiti: RMI Di Nicola Milella Al fine di toccare con mano queste tecnologie e capirne i rispettivi pro e contro si è deciso di sviluppare un’applicazione distribuita sfruttando RPC e RMI , scrivendola rispettivamente in C e in Java. In particolare quest’ applicazione ha il compito,banale, di eseguire delle operazioni matematiche sugli elementi di un vettore per poi calcolarne la media. Se avessimo trattato un’applicazione per uso locale, quindi con chiamate a procedura locali, avrebbe avuto davvero poco senso anche solo parlarne. Infatti, l’utilità di questa sperimentazione si basa sulla comprensione degli aspetti principali che caratterizzano il calcolo distribuito, in particolare delle chiamate a procedura remote gestite rispettivamente da RPC e Java RMI, e non del compito specifico. In principio si era concordato di calcolare soltanto la media degli elementi ma, considerati i tempi di esecuzione molto ridotti, è stato aumentato il carico computazionale aggiungendo il calcolo di altre operazioni (arcotantangente,potenza,radice) sugli elementi del vettore ottenendo tempi più larghi(nell’ordine dei millisecondi). Si è cercato, per quanto possibile, di rendere il codice dei due programmi server quanto più simile possibile onde evitare imprecisioni delle misurazioni derivanti dall'esecuzione di più o meno istruzioni (es. eliminando la stampa degli elementi del vettore si è scesi da circa 60ms a circa 5ms). In questa parte della relazione si introducono le Java RMI che utilizzano l’ambiente di elaborazione omogeneo costituito dalla Java Virtual Machine (JVM). In Java RMI un’applicazione è costituita da processi server che creano oggetti accessibili remotamente, registrandone il nome nell’apposito registro RMIregistry e si pongono in attesa di invocazione dei metodi sugli oggetti remoti da parte dei clienti. Un client ottiene il riferimento ad un oggetto, effettuando una ricerca nel registro RMIregistry sull’host remoto e successivamete invoca i metodi remoti. E’ il middleware RMI che si occupa dei dettagli della comunicazione: agli occhi del utente l’invocazione remota appare come un’invocazione standard di un metodo su un oggetto nel linguaggio Java tradizionale. Nell’invocare un metodo, è possibile scambiare oggetti come parametri o valori di ritorno; RMI fornisce anche i meccanismi necessari per caricare su richiesta il codice di un oggetto, e per serializzarne e trasmetterne lo stato. Lo strato middleware della piattaforma Java basato sulla tecnologia RMI (Remote Method Invocation) è molto simile al middleware RPC. Implementazione di Rmi In questo paragrafo si introducono tutti gli strumenti necessari per creare ed eseguire un’applicazione java rmi. In questo caso non è necessario scrivere un file dei servizi utilizzando un linguaggio IDL, come accade in RPC, in quanto l’IDL è proprio java. Compito dell’utente L’utente ha il compito di scrivere una classe client, in questo caso, la classe contenuta nel file RmiClient.java contiene le chiamate ai metodi remoti. Inoltre l’utente deve scrivere una classe server anche in questo caso inserita nel file RMIServerImpl.java nella quale troviamo oggetti che saranno eseguiti in remoto come in RPC (in quel caso i file erano client.c e server.c). La struttura dei due programmi è conforme allo stile di programmazione java. Rispetto ad un programma scritto in java tradizionale è necessario importare i nomi del package java.rmi. Descrizione di RmiClient La classe RmiClient contiene : … String registration = "rmi://" + registry +"/testrmi"; Remote remoteService = Naming.lookup (registration); RmiIfaces service = (RmiIfaces) remoteService; … il quale interroga l’ rmiregistry per vedere se il servizio testrmi è in esecuzione sull’host richiesto come in RPC il client attraverso la clnt create(...) interroga il portmap sull’host passato come parametro; attraverso l’invocazione del metodo ... = service.mediarmi(vett)... (notare che per l’utente è come chiamare un metodo tradizionale java in locale) avviene la connessione alla diretta alla classe RmiServer sull’host remoto. Descrizione di RmiRegistration In questo caso le differenze sostanziali consistono nell’importare i nomi dei package java.rmi e java.rmi.server, e nel chiamare il metodo Naming.rebind(registration, service ) che comunica all’rmiregistry che tale oggetto è disponibile. Inoltre, non meno importante, tutti i dati coinvolti devono essere serializzati. Quando si instaura un collegamento virtuale tra stub e skeleton (o stub e server) questo è un tipo di collegamento sequenziale, per questo tali dati devono essere serializzati. Compilazione con rmic Per supportare la trasparenza della rete in JavaRMI come in RPC c’è un compilatore, rmic, che genera lo stub e lo skeleton dell’oggetto remoto . L’unica differenza tra i due compilatori è che rpcgen compila un file scritto in un linguaggio simile al C (file.x) mentre rmic compila direttamente i file (server) generati dal compilatore javac (file.class). Notare che in questo caso l’IDL è proprio il linguaggio java. Una volta avvenuta la compilazione per eseguire il client e il server si utilizza il comando java nome file da eseguire come nelle tradizionali applicazioni java. Dalla versione 2.0 del JDK il file di skeleton non viene più generato. Lo stub generato da rmic fornisce una simulazione locale sulla JVM del client dell’oggetto remoto, inoltre non viene inviato direttamente l’oggetto ma una rappresentazione serializzata e successivamente, sulla JVM target viene ricreata una copia identica dell’oggetto (deserializzazione). Nell’appendice B sono riportate le istruzioni di compilazione per il caso di studio in questione. Rmiregistry Nel caso di Java RMI come in RPC viene utilizzatto un servizio binder, per avere a disposizione tutti gli oggetti disponibili su un nodo. rmiregistry è un’applicazione che si occupa di registrare all’interno di un registro il nome logico dell’oggetto remoto dalla classe Naming presente nel package java.rmi. Come nel caso del portmap RPC l’oggetto server si registra su rmiregistry e il client può interrogare tale applicazione per vedere se l’oggetto è disponibile su quel nodo. Come il portmap di RPC, rmiregistry e sempre in ascolto su una porta, che di default è la 1099. Test e risultati In quest’ultima parte viene spiegato il test effettuato e i risultati ottenuti, in linea con le aspettative. Di seguito sono elencate le condizioni, l’ambiente di test, mezzi utilizzati ecc: Ambiente di test: rete locale casalinga Collegamento: ethernet Macchina Server: toshiba satellite m70 – cpu Intel centrino 1.73 ghz – ram 1gb – S.O. Ubuntu 12.04 32bit Condizioni: programma server eseguito subito dopo l'avvio completo della macchina evitando di avviare altre applicazioni; carico di rete costante ridotto agli unici host coinvolti nel test Misurazioni: effettuate via codice, nei rispettivi programmi client, catturando il tempo pre e post esecuzione dell’istruzione di chiamata di procedura remota. Registrate in un file di testo automaticamente e successivamente importate in excel per eseguire i calcoli. Richiesta: è stato scelto di passare vettori da 2000 elementi. Risultati: su un campione di 10000 richieste è emerso che i tempi di esecuzione di una chiamata di procedura remota gestita da RPC sono nettamente inferiori(meno della metà, precisamente 2,127608745 in rapporto) dell’analoga chiamata gestita da Java RMI. Infatti, in studi ben più articolati, RPC viene considerato come baseline per comparare le altre tecniche e risulta essere, sotto altre condizioni di test, addirittura dalle 4 alle 6 volte più performante di java RMI come si evince dall’articolo “A PERFORMANCE EVALUATION OF RPC, JAVA RMI, MPI AND PVM” di Kalim Qureshi (Department of Math and Computer Science University of Kuwait ) e Haroon Rashid (COMSATS Institute of Information Technology Abbattabad Pakistan). In particolare è emerso che RPC, nel trattare una richiesta (tempo totale), ha impiegato mediamente 2,179 ms mentre Java Rmi 4,636 ms. Il tempo, come detto precedentemente, è stato misurato lato client considerato che nell’ambiente in cui si è svolto il test il tempo di trasmissione della richiesta è stato trascurabile. Quindi il tempo totale è per lo più formato dal tempo di esecuzione, composto a sua volta da: 1) Tempo delle operazioni preliminari sulla richiesta ricevuta 2) Tempo di esecuzione della procedura 3) Tempo di costruzione della risposta Il tempo di esecuzione della procedura/metodo remota è risultato essere costante per entrambi gli approcci mantenendo la differenza derivante alla maggior efficienza del programma server scritto in C (un vantaggio intrinseco di Rpc). Installazione/configurazione ambiente java rmi su ubuntu Verificare la presenza di un'istallazione java (Openjdk o Oracle-java) con il comando java -version, in caso non fosse installato neanche uno dei due ambienti procedere all'installazione tramite il software center per Openjdk, mentre per Oracle-java importare un ppa che contenga tutti i pacchetti necessari e proseguire all’installazione. In questo caso l'ambiente utilizzato è java 7 di Oracle. Compilazione 1) javac RmiIface.java RmiSrvImpl.java 2) javac RmiRegistration.java 3) javac RmiClient.java Esecuzione 1) java RmiRegistration <host> sulla macchina server 2) java RmiClient <server host> <dimensione vettore> <path file di report> <numero richieste> sulla macchina client Normalmente è necessario avviare il processo rmiregistry o quanto meno accertarsi che sia in esecuzione altrimenti bisogna avviarlo tramite il comando rmiregistry &, ma in questo caso è stato gestito tutto da codice; alla terminazione del server terminerà anche rmiregistry in quanto sotto processo del server, quindi se si necessita tenerlo sempre in esecuzione bisogna avviarlo da linea di comando.