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.