Il contattamento degli Slave è sicuramente la parte centrale nell'implementazione del Master. Punto fondamentale di
tutto il sistema è infatti coordinare il lavoro degli Slave modo da garantire
uno strumento di ricerca sicuro e affidabile, sempre però tenendo conto
dell'ambiente in cui si lavora (Internet) e del tipo di servizio che si vuole
offrire, sicuramente di tipo "Best Effort".
FUNZIONAMENTO NORMALE:
Mediante un apposito
oggetto presente nel master (SlaveVivi) viene tenuto traccia degli Slave
"vivi" presenti nel sistema locale, univocamente determinati da IP e
porta (in tal modo è stato possibile gestire una rete locale su una unica
macchina).
Mediante un oggetto private ("contactSlave") costruito su una HashTable (vedi figura) viene tenuto conto, per ogni slave, se ha risposto (con un flag) e del numero di tentativi di comunicazione effettuati. Tale oggetto è costituito da una tabella in cui si utilizza il "nome" dello Slave KeySlave come chiave di ricerca e come campo una classe d'appoggio formata semplicemente da un intero "numero di trasmissioni" ed un flag "ack arrivato":
A
tali slave, N, viene inoltrata la richiesta, composta dall'intera richiesta
del Client (o dello Slave) + la porta su cui dovranno mandare il risultato della ricerca (per la corretta gestione delle porte UDP e
TCP nel caso reale di più Query concorrenti).
dopo le N Send con protocollo UDP si attendono altrettanti Acknoledge. Dopo un certo timeout settato inizialmente, in generale arrivano solo N1 (< N) Ack e nei rispettivi record dell'oggetto "contactSlave" viene posto il Flag a True per identificare che hanno risposto. Negli altri viene incrementato il contatore dei tentativi. Se alcuni rispondono che sono congestionati non si considerano più, si tolgono da "contactSlave" (ma solo per quella richiesta, non si elidono dal sistema);
Se è
possibile ricevere altri Ack (cioè alcuni slave con Flag false e contatore <
valore di soglia predeterminato presente in Constans.nTrasmMax) si ricomincia
considerando come nuovo N il numero degli Slave ancora "vivi" in
"contactSlave;
Alla fine (o tutti Flag true o alcuni False ma
contatore > valore massimo) N2 (<N) Ack sono arrivati,
vuol dire che N2 Slave sono "vivi" e hanno cominciato a "lavorare";
(N-N2)
Ack non sono arrivati quindi si "eliminano" dal suddetto oggetto che tiene traccia degli slave vivi ("SlaveVivi");
Comunico alla Cache il numero N2 di Ack da aspettare, funzione necessaria in caso di rielezione;
Per
gli N2 Slave si aspettano i relativi Ack, che arriverà quando hanno il file pronto per
essere trasferito...
Quando
arriva un "Ack: ho il file" da uno Slave sulla porta deputata a tale
compito (diversa per ogni richiesta e diversa dalla porta di comunicazione
iniziale sulla quale e' stata solo spedita la richiesta) viene creato un Thread
"scaricaFile" collegato al padre mediante pipe che
preleva il file, questo per ogni Ack arrivato;
Tale Thread scarica il file via TCP; per il protocollo vedere la restituzione dei dati da parte dello Slave al Master;
quando
un Thread "scaricaFile" ha finito di prelevare il file
lo comunica al padre via pipe e, dopo una risposta da parte di
quest'ultimo, termina la sua esecuzione.
Quando tutti hanno finito e i file sono "pronti" il metodo inoltraQuery e tale protocollo di contattamento sono conclusi.
Il controllo torna quindi al metodo principale del Thread ("run") che deve restituire il risultato al chiamante. A questo punto abbiamo come risposta un numero variabile di File provenienti dai diversi Slave, che potrebbero anche contenere, in parte, informazioni identiche. Al fine di non restituire al mittente risultati ridondanti è stato ritenuto opportuno "ripulire" i dati ricevuti, ottenendo il duplice vantaggio di fornire risultati più "puliti" e, nel contempo, risparmiare in occupazione di banda nell'eventuale collegamento TCP (caso di richiedente Slave). A tale risultato si arriva mediante una apposita classe (FondiFile) il quale provvede a compattare i file in uno solo, eliminando nel contempo le righe identiche (mediante un semplice algoritmo sicuramente migliorabile) e ritornando il nome del file (univoco per ogni richiesta). E' ora possibile restituire i dati, verrà quindi spedita una Email al Client oppure verrà dato il file agli Slave via TCP chiamando il metodo opportuno.
Infine si decrementa il numero di richieste attive e si termina l'esecuzione.
FUNZIONAMENTO IN CASO DI RIELEZIONE:
Esiste anche il caso che il thread di "inoltro" agli Slave sia stato generato all'atto di una ri-elezione del Master, cioè al fine di ottenere dati per una richiesta già inoltrata agli Slave dal Master "precedente". In tal caso non bisogna inoltrare il pacchetto di richiesta, bensì bisogna solamente rimanere in attesa delle risposte degli Slave che hanno concluso la ricerca. Il protocollo parte quindi dal seguente punto. Per sapere il numero N2 di "Ack" da aspettare (non in modo preciso ma un limite massimo) ogni volta che una richiesta è stata accettata da N2 slave, lo "comunico" alla cache aggiornandola con tale parametro, che verrà propagato anche agli Slave (si veda la sezione relativa).
NOTE:
Mediante
timeout delle socket viene tenuto in conto che alcuni Ack potrebbero non
arrivare mai, in qualsiasi fase del protocollo... ciò quindi non blocca mai il
sistema che può sempre reagire.
Se
vi sono errori tali da non consentire la formazione del file, viene
restituito una stringa "errore" e non si abortisce brutalmente,
mantenendo quindi il servizio e dando un'indicazione al mittente.
La
gestione delle porte è stata veramente critica: per ogni richiesta
contatto gli N Slave su 1 porta, diversa per ogni richiesta e deducibile dall'indicatore univoco della richiesta all'interno del Master.
Creo quindi l'ulteriore porta sulla quale gli slave con il file da restituire devono rispondere (e che comunico loro in fase di inoltro della richiesta); questo per evitare interferenze fra Slave che rispondono alla richiesta e Slave che hanno già concluso il lavoro.
gli Slave che non hanno risposto subito agli Ack iniziali non possono poi reinserirsi nel soddisfacimento della richiesta corrente in quanto devono re-iscriversi nel sistema;