MokaByte Numero 35  - Novembre 99
Questo progetto è stato realizzato da
  Fabio Cesari e
 Massimo Campeggi 
per il corso di Reti di Calcolatori (A.A. 98/99) tenuto dal prof. Antonio Corradi presso la facoltà di ingegneria informatica dell'Università di Bologna.
UML e lo sviluppo 
del software
E' possibile scaricare la versione compressa di questa presentazione (375Kb in formato zip).

  
INDICE DEI CONTENUTI:
Prima parte:
Seconda parte: 
(nel prossimo numero di MokaByte)
  • ANALISI DELLE PRESTAZIONI
  • METODO DI CALCOLO
  • CONCLUSIONI

DFG - Distributed Fractal Generator

DFG è una applicazione distribuita per la creazione di immagini dell'insieme di Mandelbrot. La generazione delle immagini avviene sfruttando le risorse di calcolo di varie workstation, secondo lo schema seguente: Struttura di DFG


 
 
 
 
 
 
Nella struttura dell'applicazione si possono distinguere tre diverse entità:
DFGClient I compiti di ciascun client presente nel sistema sono i seguenti: 
  • Permettere all'utente di inserire i parametri che descrivono l'immagine che vuole ottenere
  • Inviare richieste di servizio a DFGServer
  • Ricevere e gestire risposte da DFGServer
  • Visualizzare le immagini ricevute da DFGServer (in caso di terminazione del servizio con successo) o gli eventuali messaggi di errore
  • Permettere all'utente di interrompere il calcolo di un'immagine e ottenere il risultato parziale del calcolo
E' da notare il fatto che la presenza dei server DFGSlave è completamente invisibile ai client, i quali interagiscono unicamente col server principale. 

Note sull'implementazione: i client sono realizzati come applet Java e sono accessibili da una pagina web residente sul nodo su cui opera DFGServer. Alternativamente, i client possono essere applicazioni Java (in questo caso, sulla linea di comando si dovrà specificare l'host su cui risiede DFGServer).

DFGServer E' il server principale. I suoi compiti sono i seguenti: 
  • Interagire con i client, ricevendo le richieste di servizio e spedendo le relative risposte
  • Smistare il lavoro tra gli slaves disponibili (vedere sezione Bilanciamento del carico)
  • Ricevere i risultati dagli slaves e comporre le risposte per i client
  • gestire eventuali richieste di interruzione di un calcolo
Note sull'implementazione: il server è un'applicazione Java e usa vari thread per poter svolgere i suoi compiti in modo concorrente.
DFGSlave I compiti di ciascun DFGSlave sono i seguenti: 
  • Ricevere richieste di servizio da parte del server principale
  • Elaborare le richieste di servizio ricevute e produrre una risposta
  • Inviare la risposta al server principale
  • gestire eventuali messaggi di richiesta di interruzione di un calcolo
Note sull'implementazione: gli slaves sono applicazioni Java. Per motivi di efficienza, il calcolo dell'immagine viene svolto utilizzando una funzione nativa. Si è scelto di realizzare gli slaves come servitori paralleli (le motivazioni di questa scelta sono spiegate nella sezione Specifiche degli slaves).


DFG - Specifiche
    La comunicazione tra le entità presenti nel sistema avviene tramite gli stream Java, utilizzando le socket con connessione per implementare il concetto di stream. La scelta degli stream rispetto alla comunicazione con datagrams è motivata dal fatto che in questo sistema la comunicazione tra le entità avviene esclusivamente tramite lo scambio di oggetti, che (soprattutto nel caso di trasmissione di immagini) possono avere dimensioni considerevoli e necessitano di canale di comunicazione affidabile e che mantenga l'ordine dei messaggi.
DFGClient:
    L'invio di una richiesta di servizio consiste nella spedizione a DFGServer di un oggetto della classe Messaggio_Mandelbrot contenente i parametri necessari alla generazione dell'immagine.

    I controlli sulla correttezza dei dati inseriti in una richiesta vengono effettuati dal client (e vengono quindi trascurati sia dal server che dagli slaves).

    Dopo l'invio di una richiesta di servizio, il client rimane in attesa di una risposta in modo sincrono. Viene però data la possibilità all'utente di interrompere l'operazione in corso in qualunque momento.

    Prima dell'invio di una richiesta di un calcolo e all'atto della ricezione di una risposta (anche parziale) o al verificarsi di un errore, viene disabilitata la funzione di invio di richieste di interruzione.

    Dopo l'invio di una richiesta, non è possibile spedirne un'altra fino alla ricezione di un risultato (anche parziale) o al verificarsi di un errore.

    Nel caso l'utente voglia interrompere il calcolo, viene spedito a DFGServer un messaggio di tipo Messaggio_Interruzione. Non si è ritenuto oppurtuno introdurre un time-out ma si è scelto di lasciare all'utente la possibilità di interrompere il calcolo di un'immagine per due motivi:

      l'applicazione nel suo complesso è pensata per essere utilizzata interattivamente, non per generare immagini in modalità batch.

      la corretta scelta di un time-out è difficile e potrebbe contrastare con i desideri dell'utente.

    Nel caso l'operazione vada a buon fine, la risposta del server è l'immagine richiesta, che viene visualizzata da DFGClient in un apposito frame.

    Nella versione definitiva di DFGClient, si è modificata la struttura del codice in modo da poterlo eseguire sia come applet che come applicazione. Quando è  usato come applicazione, è necessario specificare l'host di DFGServer sulla linea di comando: java dfg.dfg_client host_DFGServer

    A causa delle restrizioni imposte dai security manager utilizzati dalle varie versioni di browser e appletviewer provate, a volte l'applet non è in grado di determinare il nome dell'host su cui sta eseguendo. Per questo motivo, si utilizza un text field nel quale l'utente può inserire il nome dell'host locale (ovviamente questo campo viene riempito automaticamente quando DFGClient è utilizzato come applicazione). D'altra parte, questa informazione è utilizzata solo per costruire l'identificatore delle richieste inviate e ha il solo scopo di permetterne il riconoscimento per una eventuale interruzione. Per questo motivo, si può inserire una qualsiasi stringa che, assieme al numero di porta utilizzato per inviare le richieste, costituisca un identificatore unico nel sistema.

    L'applet è a conoscenza dell'host su cui opera DFGServer in quanto questo coincide con l'host da cui l'applet stesso è stato prelevato. Nel caso l'applet venga caricato da hard disk anzichè tramite un server web (questo accade sulle macchine lia, dove non è installato un server web), questa informazione è però sconosciuta all'applet e va quindi inserita dall'utente ; inoltre, in questa situazione il security manager dell'applet permette solo di collegarsi a localhost, quindi l'applet deve essere in esecuzione sullo stesso nodo sul quale è presente DFGServer. Ovviamente, durante il normale funzionamento (cioè quando l'applet viene scaricato da un server web oppure DFGClient è utilizzato come applicazione), il text field viene riempito automaticamente.

    L'applet è a conoscenza del numero di porta sul quale DFGServer è in attesa di richieste di servizio (si è scelta la porta 20000).

DFGServer:

Per poter suddividere il calcolo di una immagine tra gli slaves, DFGServer deve mantenere una struttura dati (chiamata Tabella) in cui memorizzare gli slaves attualmente disponibili, la loro collocazione (nome dell'host e numero di porta) e la loro situazione di carico. Vedere la sezione Bilanciamento del carico per le specifiche relative alle modalità di scelta degli slaves da utilizzare.

DFGServer deve essere in grado di servire vari tipi di richieste. Ogni tipo di richiesta corrisponde ad un diverso tipo di Messaggio:

    Messaggio_Mandelbrot: Viene utilizzato dai DFGClient per richiedere la generazione di un'immagine. Il servizio di questo tipo di richiesta viene svolto creando un oggetto GestoreRichiesta che permette di effettuare la ripartizione del carico, ricevere le risposte dai vari slaves e spedire la risposta al client.

    Messaggio_Interruzione: Viene utilizzato dai DFGClient per richiedere l'interruzione del calcolo di una immagine. Il servizio di questo tipo di richiesta viene svolto creando un oggetto GestoreInterruzione che permette di ritrovare gli slaves interessati dall'operazione e inviare loro una richiesta di interruzione.

    Messaggio_Registrazione: Viene utilizzato dai DFGSlave per registrarsi nella Tabella.

    Messaggio_Verifica: Viene utilizzato dai DFGSlave per verificare periodicamente la presenza di DFGServer.

Operazioni svolte da DFGServer alla ricezione di un messaggio di tipo Messaggio_Mandelbrot
Bisogna innanzitutto osservare che ogni messaggio di questo tipo contiene un elemento (ident) che lo identifica univocamente e il cui formato è "host_DFGClient : porta_DFGClient". Questa informazione è utilizzata solo per permettere il riconoscimento delle richieste di servizio per una loro eventuale interruzione. 
Se non ci sono slaves disponibili, DFGServer comunica al client che il servizio non è attivo (chiudendo la connessione con esso). In caso contrario, si procede come segue: 
Per prima cosa viene creato un oggetto GestoreRichiesta, che viene utilizzato per scandire la Tabella, crearne una copia contenente l'indicazione del numero di righe che ciascuno slave deve calcolare, allocare un'area di memoria per l'immagine, aggiornare la Tabella con la nuova situazione di carico e creare, per ogni slave, un thread Esecutore che si occupi di inviargli la richiesta e ricevere la corrispondente risposta. 
I thread Esecutore vengono assegnati ad un gruppo di thread il cui nome identifica univocamente il servizio che stanno svolgendo (viene usato ident come nome per il gruppo) e viene dato loro un nome che permetta, in caso di necessità (ved. la gestione di una richiesta di interruzione), di ritrovare lo slave che devono contattare (il formato del loro nome è: "host_DFGSlave : porta_DFGSlave"). La figura seguente mostra un possibile schema dei gruppi di thread e dei thread in essi contenuti durante l'esecuzione di DFGServer:

  
 
 
 
 

Nel caso la richiesta vada a buon fine, ogni thread Esecutore ottiene la porzione di immagine che gli era stata commissionata e la memorizza nella posizione corretta dell'area di memoria appositamente predisposta dall'oggetto GestoreRichiesta che lo ha generato. Quando tutti i thread Esecutore hanno terminato il proprio compito, l'immagine ottenuta viene trasmessa al client.

Nota: il fatto di non realizzare GestoreRichiesta come thread (come si era fatto nelle versioni preliminari di DFG) ma solamente come oggetto di cui vengono invocati dei metodi introduce una serializzazione delle operazioni svolte da DFGServer, il quale non si mette in attesa di nuovi messaggi prima di aver creato tutti i thread Esecutore e aggiornato la Tabella. Questo permette di risolvere a priori possibili situazioni di corsa critica. Questa soluzione non provoca problemi di ritardo nel servizio, visto che le operazioni svolte tramite GestoreRichiesta sono tutte operazioni locali. Per lo stesso motivo, anche i thread GestoreInterruzione sono stati modificati nello stesso modo.

Operazioni svolte da DFGServer alla ricezione di un messaggio di tipo Messaggio_Interruzione:
In caso di interruzione volontaria del calcolo da parte dell'utente, si potrebbe semplicemente lasciar continuare calcolo dell'immagine da parte degli slaves, ignorandone la risposta. Questo approccio può però provocare un sovraccarico del sistema e si ritiene quindi oppurtuno che i DFGSlave interessati al calcolo dell'immagine vengano interrotti. 
All'atto della ricezione di un Messaggio_Interruzione, viene creato un oggetto GestoreInterruzione che permette di estrarre dal messaggio un identificatore (ident) che corrisponde al lavoro da terminare e scandire la lista dei gruppi di thread presenti nel gruppo di thread principale (main), cercando il gruppo avente per nome ident. Questo gruppo contiene i thread Esecutore relativi al lavoro da terminare. Viene quindi creato, per ogni Esecutore trovato, un thread EsecutoreInterruzione che ha il compito di spedire allo slave corrispondente un Messaggio_Interruzione. I thread EsecutoreInterruzione vengono inseriti nel gruppo main.

Il procedimento di interruzione di un calcolo può essere realizzato in modi diversi da quello scelto (ad esempio riducendosi semplicemente a far terminare i thread Esecutore e GestoreMandelbrot coinvolti) . I pregi del metodo scelto sono la semplicità dei protocolli di comunicazione e la considerazione che la ricezione di un risultato (anche se parziale) del calcolo da parte di DFGClient è importante. 
 
Esempio di risultato parziale del calcolo
Risultato parziale di un calcolo

 

    Tutte le operazioni di modifica della Tabella da parte dei vari thread devono avvenire in modo atomico (=>uso di metodi synchronized). L'esecuzione concorrente dei metodi public contenuti nella classe Tabella può infatti portare a stati inconsistenti della Tabella.

    In caso di ricezione di un Messaggio_Interruzione relativo ad un job non presente, non si fa nulla. Questa situazione può verificarsi se il calcolo dell'immagine termina prima che il messaggio di interruzione abbia il tempo di arrivare a DFGServer ed essere eseguito.

    Se non si trova il gruppo di thread da interrompere (può accadere se durante la spedizione e il trattamento del comando di interruzione il calcolo termina), si prosegue l'esecuzione senza altre conseguenze.

    Se l'aggiornamento della Tabella al termine di un calcolo è in ritardo rispetto alla ripartizione di un nuovo lavoro, questo viene suddiviso tra gli slaves utilizzando una situazione di carico inesatta. Questo non può però essere considerato un errore perchè, anche se il carico effettivo degli slaves non è quello indicato in Tabella, il lavoro che stavano svolgendo non è ancora terminato del tutto: manca l'aggiornamento della Tabella, che deve essere considerata come l'ultima parte del lavoro.

    E' possibile, in teoria, che una richiesta di interruzione arrivi agli slaves interessati prima della corrispondente richiesta di calcolo, risultando quindi inefficace. Questo può accadere se i thread Esecutore sono molto più lenti a contattare gli slaves dei corrispondenti thread EsecutoreInterruzione (nonostante questi siano stati creati sicuramente dopo di essi). Anche per prevenire questa remota possibilità si è previsto di poter inviare più messaggi di interruzione durante il calcolo (vedere anche la sezione successiva).

Linea di comando: java dfg.dfg_server

DFGSlave:

    All'avvio, ogni DFGSlave provvede a contattare DFGServer e a dichiarare la propria disponibilità a ricevere richieste di servizio, indicando un valore iniziale di carico che rappresenta il proprio carico di default. Questo parametro deve poter essere specificato come argomento all'atto dell'esecuzione del programma e non è previsto che possa essere cambiato durante l'esecuzione (se verrà ritenuto necessario si potrà aggiungere questa caratteristica). Il significato di questo parametro è spiegato nella sezione Bilanciamento del carico.

    L'atto di registrazione presso DFGServer avviene spedendo un Messaggio_Registrazione contenente le informazioni necessarie ad inizializzare una riga della Tabella.

    Ad intervalli di tempo prefissati, DFGSlave si accerta che DFGServer sia attivo spedendogli un Messaggio_Verifica.

    Ogni DFGSlave presente nel sistema deve essere a conoscenza dell'host su cui opera DFGServer e del numero di porta su cui esso è in attesa di richieste.

DFGSlave deve essere in grado di servire due tipi di richieste. Ogni tipo di richiesta corrisponde ad un diverso tipo di Messaggio:
    Messaggio_Mandelbrot: Viene utilizzato da DFGServer per richiedere il calcolo di immagini.

    Messaggio_Interruzione: Viene utilizzato da DFGServer per richiedere l'interruzione del calcolo relativo ad una particolare immagine.

Operazioni svolte da DFGSlave alla ricezione di un messaggio di tipo Messaggio_Mandelbrot
Alla ricezione di una richiesta di questo tipo, DFGSlave crea un thread GestoreMandelbrot che si occupa di servirla; il thread estrae dalla richiesta i parametri, alloca un'area di memoria per l'immagine risultante e chiama la funzione nativa per il calcolo dell'immagine. Al thread viene assegnato come nome l'identificatore (ident) contenuto nel messaggio stesso (nota: il formato di ident non ha importanza, basta che permetta di identificare univocamente il lavoro che sta svolgendo). Questo è necessario per poter servire una eventuale richiesta di terminazione del calcolo proveniente da DFGServer. Il motivo della scelta di avere slaves concorrenti è determinato da due fattori:
    le risorse di calcolo di una macchina dotata di più CPU non verrebbero sfruttate da una implementazione sequenziale degli slaves. (questa limitazione potrebbe comunque essere superata eseguendo più istanze del programma DFGSlave, ma sarebbe una soluzione meno elegante).

    se gli slaves fossero sequenziali, nel caso DFGServer ricevesse un numero di richieste di servizio superiore agli slaves disponibili, dovrebbe attendere che almeno uno degli slaves si liberasse prima di poter iniziare a servire tutte le richieste. L'implementazione parallela degli slaves permette invece di poter sempre iniziare il servizio di una nuova richiesta (=>possibile migliore throughput).

Operazioni svolte da DFGSlave alla ricezione di un messaggio di tipo Messaggio_Interruzione
DFGSlave estrae dal messaggio il parametro ident e scandisce la lista dei thread contenuti nel gruppo main, uccidendo tutti i thread aventi per nome ident (è possibile che ci sia più di un thread chiamato ident se ci sono state ridistribuzioni di carico a seguito della caduta di uno o più slaves). Prima di terminare, ciascuno dei thread interessati spedisce il risultato parziale del calcolo a DFGServer. In questo modo, DFGClient riceve un'immagine incompleta ma che può ugualmente essere utile. 
Nota: l'invio da parte di DFGClient di una richiesta di interruzione può (in casi molto particolari) aumentare il tempo di generazione di un'immagine. Ad esempio, se il messaggio di interruzione arriva a DFGSlave quando il calcolo è già stato completato ma prima (o durante) la spedizione a DFGServer del risultato, si interrompe la connessione tra DFGSlave e DFGServer e quindi si provoca una ridistribuzione di carico (e i nuovi slaves interessati non verranno interrotti). Per questo motivo si è prevista la possibilità di poter inviare più di una richiesta di interruzione da parte di DFGClient (anche se in pratica queste situazioni non si sono mai verificate).

Linea di comando: java dfg.dfg_slave porta_slave host_server carico_di_default


DFG - Bilanciamento del carico

Problemi di bilanciamento del carico: 
Come già sottolineato, DFGServer deve mantenere una struttura dati (chiamata Tabella) in cui memorizzare gli slaves attualmente disponibili, la loro collocazione (nome dell'host e numero di porta) e il loro carico attuale. Un esempio di tale struttura dati è il seguente:

Tab.1
nome host numero porta carico
lia09 20000 176800000
lia07 20001 176800000
lia06 20001 100000000

Nota: l'implementazione parallela degli slaves rende inutile l'esecuzione di più di una istanza di DFGSlave per ogni host e permette di identificare ogni DFGSlave con l'host su cui opera. Nel sistema realizzato, comunque, viene utilizzata la coppia nomehost : numeroporta per individuare uno slave.

Definizione del  parametro "carico"
Per poter quantificare il carico a cui è sottoposto uno slave si deve innanzitutto definire una regola per assegnare ad ogni richiesta di servizio un numero intero che ne rappresenti il costo, vale a dire una stima delle risorse di calcolo necessarie per servire la richiesta. Questo valore deve tenere conto dei seguenti parametri:

    dimensioni dell'immagine da calcolare

    numero massimo di iterazioni da utilizzare

Una stima del costo di una richiesta di servizio in cui l'immagine da calcolare sia di 640x480 pixel con un numero massimo di iterazioni pari a 500 è data da 640*480*500 = 153,6E6. Questa stima è molto inaccurata perchè presuppone il fatto che ogni pixel dell'immagine per essere calcolato richieda un numero di iterazioni pari al numero massimo di iterazioni. D'altra parte, questa stima fornisce un valore che permette di confrontare due richieste diverse con un grado di approssimazione che è ritenuto accettabile.

Il parametro "carico" è definito come la somma del costo delle richieste servite da un determinato slave in un certo istante. Al numero ottenuto viene anche sommato un valore di default comunicato dallo slave all'atto della sua registrazione nella Tabella. Quest'ultimo valore deve essere specificato manualmente all'atto della esecuzione dello slave e serve a tenere conto del tipo di macchina su cui lo slave sta eseguendo e delle risorse che sono disponibili su di essa. Non è previsto che questo "handicap" possa essere modificato durante l'esecuzione dello slave. La scelta di questo valore (peraltro opzionale) deve essere effettuata sulla base della conoscenza delle macchine su cui verranno eseguiti gli slaves.

Esempio (ved. tab.1): 
lia06, lia07 e lia09 hanno tutti un carico di default pari a 100E6. lia06 non sta calcolando, mentre lia07 e lia09 stanno calcolando (metà ciascuno) un'immagine di 640x480 pixel con un numero massimo di iterazioni pari a 500.


Algoritmo per la suddivisione del lavoro tra gli slaves:

Definizione del problema
data una richiesta di generazione di un'immagine di dimensioni WxH e la Tabella qui sopra descritta, questo algoritmo restituisce l'elenco degli slaves da utilizzare e il numero di righe dell'immagine che ciascuno di essi deve calcolare.

Considerazioni preliminari
il numero di righe da assegnare a ciascuno slave non può che basarsi sull'indicazione di carico contenuta nella Tabella. Questo non garantisce, qualunque algoritmo venga scelto, che la ripartizione del carico sia "equa". Alcune parti dell'immagine possono infatti richiedere un tempo molto superiore per essere calcolate rispetto ad altre.

Si sceglie di utilizzare sempre tutti gli slaves disponibili, evitando di suddividere tra più slaves il servizio di richieste con un costo inferiore a 100000 (piccole richieste possono verificarsi nel caso in cui uno slave fallisca e si debba ripartire il suo lavoro tra gli altri).

L'algoritmo ideale dovrebbe far sì che, dopo l'assegnazione del numero di righe da calcolare ai vari slaves, il carico di ciascuno slave fosse il medesimo. 
L'algoritmo scelto NON garantisce questo, ma ha il pregio di essere molto semplice e quindi molto veloce. Considerando il fatto che la stima del costo di un calcolo è molto approssimata, utilizzare un algoritmo di ripartizione del carico più complicato non porterebbe a miglioramenti significativi nelle prestazioni del sistema.

Algoritmo
Se il costo della richiesta è inferiore a 100000, viene assegnata allo slave con il valore più basso di carico presente nella Tabella. In caso contrario, si procede come segue: 
La Tabella viene scandita e ne viene creata una copia in cui al posto del carico c'è il numero di righe da far calcolare allo slave corrispondente. 
Sia CARICO_TOTALE la somma delle voci "carico" della Tabella e N il numero di righe della Tabella. 
Detto Cj il carico corrente di uno slave, il numero di righe da assegnargli è dato dalla formula:

N_RIGHE = parte_intera( ( 1 - Cj / CARICO_TOTALE )  * H /  ( N -1 ) )

Ovviamente uno slave non viene utilizzato se, a causa dell'arrotondamento, si ha per esso N_RIGHE == 0. 
Per evitare i problemi dovuti all'arrotondamento introdotto dalla formula, allo slave presente nell'ultima riga della Tabella viene imposto di calcolare le righe non ancora assegnate agli altri slaves.

Esempio
La tabella 1 mostra una situazione in cui CARICO_TOTALE == 453,6E6 
La richiesta di un'immagine 800x600 (con un numero massimo di iterazioni pari a 1000) genera le seguenti assegnazioni:

Tab. 2
nome host numero porta numero righe da calcolare
lia09 20000 183
lia07 20001 183
lia06 20001 234

Dopo la assegnazione, la Tabella viene aggiornata e la nuova situazione è indicata nella tab.3:

Tab. 3
nome host numero porta carico
lia09 20000 323200000
lia07 20001 323200000
lia06 20001 287200000


DFG - Tolleranza ai guasti

DFGClient:

    In caso di caduta di DFGServer o della connessione con esso durante lo svolgimento del servizio o della ricezione della risposta, l'applet deve essere in grado di accorgersi del problema e segnalarlo all'utente. Non è prevista la terminazione del client, che deve dare la possibilità all'utente di attendere il ripristino del servizio e di riprovare.

    Nel caso di eccezioni durante la spedizione di un Messaggio_Interruzione, si chiude la connessione con DFGServer, si segnala l'errore e si continua l'esecuzione (non bisogna chiudere la connessione su cui DFGClient è in attesa dell'immagine, perchè questa operazione viene svolta dal punto precedente).

DFGServer:
    In caso di caduta di DFGServer, al momento del suo ripristino bisogna prevedere il ripristino della sua Tabella in uno stato consistente. Questo avviene grazie ai messaggi di verifica della presenza di DFGServer che gli slaves spediscono ad intervalli di tempo regolari. Quando DFGServer riceve un messaggio di verifica da parte di uno slave non presente in tabella, approfitta di tale messaggio per effettuare la registrazione dello slave.

    In caso di impossibilità nello stabilire la connessione con uno slave o in caso di caduta di uno slave durante il servizio, il thread Esecutore corrispondente ritorna un messaggio di errore che provoca la cancellazione dello slave dalla Tabella e la ridistribuzione del lavoro tra gli altri slaves. Nel caso in cui lo slave cancellato sia l'unico slave presente, si deve comunicare al client che il servizio non è attualmente disponibile. Attenzione: la scelta di cancellare lo slave dalla Tabella non sempre è corretta: se la caduta della connessione con DFGSlave non è dovuta al fatto che lo slave è guasto ma solo ad un errore occasionale, la cancellazione dallo slave dalla Tabella è un'operazione concettualmente sbagliata. D'altra parte, il thread Esecutore non può sapere se lo slave è ancora in grado di svolgere il suo compito e la caduta della connessione con esso lo deve indurre a ritenere che non sia più "affidabile", quindi a cancellarlo dalla Tabella. Una cancellazione errata può portare ad anomalie nel funzionamento del sistema, ma solo se lo slave, al momento della sua cancellazione, sta calcolando altre immagini e se il completamento di tali immagini avviene dopo la spedizione del successivo Messaggio_Verifica a DFGServer. In tal caso, infatti, lo slave viene reinserito in Tabella col suo carico di default, che non corrisponde al suo carico effettivo. Per risolvere questo problema, sarebbe sufficiente far sì che i messaggi di verifica contenessero il reale valore di carico e non il carico di default.

    In caso di caduta del client o della connessione con esso, DFGServer non può accorgersi di nulla se non quando tenta di spedirgli l'immagine richiesta. Di conseguenza, per evitare uno spreco di risorse, nel caso l'utente voglia interrompere il calcolo di un'immagine non deve limitarsi a far terminare il client, ma deve premere il bottone "stop".

    Se un EsecutoreInterruzione non riesce a mettersi in contatto con lo slave per il quale è stato creato (ad esempio perchè nel frattempo lo slave è caduto) o se rileva eccezioni durante la spedizione del Messaggio_Interruzione, termina senza altre conseguenze.

    In caso di ricezione di un Messaggio_Registrazione da uno slave già presente in Tabella (dovuto ad esempio al fatto che lo slave è caduto mentre era inattivo ed è stato successivamente ripristinato), la sua Entry viene sostituita con quella contenuta nel messaggio. In questo modo è possibile modificare il carico di default di uno slave.

    Dopo che tutti i thread Esecutore sono terminati, bisogna eliminare il gruppo di thread che li conteneva (utilizzando il metodo setDaemon(true)). Questo è necessario per due motivi:

      mantenere in vita un oggetto quando è diventato inutile è uno spreco di risorse

      se un client spedisce, in tempi diversi, richieste di immagini utilizzando lo stesso numero di porta (questo può accadere in caso di caduta e successivo ripristino dell'host su cui esso risiede), viene creato un secondo gruppo di thread con lo stesso nome e non si riesce a trattare correttamente un'eventuale richiesta di interruzione.

DFGSlave:
    Guasto di un DFGSlave: se al momento della terminazione lo slave non sta calcolando nessuna immagine, DFGServer non si accorge della sua scomparsa. DFGServer si accorge della sua mancanza (e aggiorna di conseguenza la Tabella) solo quando tenta di utilizzarlo per un nuovo lavoro. Questo comporta un overhead (generazione di un thread, tentativo di stabilire una connessione e ridistribuzione del carico).

    In caso di caduta della connessione con DFGServer, lo slave si limita a rilevare l'eccezione e a liberare le risorse allocate a quella connessione.

    In caso di ricezione di un Messaggio_Interruzione relativo ad un GestoreMandelbrot non presente, non si fa nulla. Questa situazione può verificarsi se il calcolo dell'immagine termina prima che il messaggio di interruzione abbia il tempo di arrivare a DFGSlave e di essere eseguito.

    In caso di fallimento della procedura di registrazione, si continua ad effettuare periodicamente la richiesta di registrazione, in attesa che DFGServer venga ripristinato.
     

    In caso di fallimento della procedura di verifica della presenza di DFGServer, si interrompono tutti i thread GestoreMandelbrot attivi e si iniziano ad effettuare periodicamente richieste di registrazione, in attesa che DFGServer venga ripristinato.

 

  
 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it