Progetto di Gottardi Gianpaolo Matr. 197814 Reingegnerizzazione di un’applicazione: da un modello single-user ad un’architettura distribuita tollerante ai guasti basata sul discovery dinamico dei servizi Reti di Calcolatori LS Ch.mo Professor Antonio Corradi Motivazioni del progetto Introdurre un’infrastruttura per rendere distribuita un’applicazione single-user preesistente contemplando politiche di: Fault tolerance, al fine di garantire il corretto funzionamento del sistema anche a fronte del guasti di un server; Discovery dinamico dei servizi per rendere scalabile la soluzione proposta; Reti di calcolatori - 1 L’applicazione originale Si tratta di un prodotto sviluppato in C# demandato alla gestione a 360° di uno studio odontoiatrico (quindi pazienti, cartelle cliniche, appuntamenti, listini, fatture, report) con particolare attenzione all’aspetto legislativo. Da tempo il committente aveva espresso l’esigenza di poter utilizzare l’applicazione all’interno di una rete locale, garantendo l’accesso a più postazioni mobili contemporaneamente. Questo naturalmente ha richiesto una profonda rivisitazione del software precedente, portando al progetto di una nuova infrastruttura al cui interno si prevede l’assegnamento delle diverse funzionalità a più server cooperanti tra loro. Reti di calcolatori - 2 Soluzione precedente Una prima (e molto semplice) realizzazione prototipale prevedeva: la condivisone di un unico database, senza considerare l’eterogeneità dei servizi; la mutua esclusione degli accessi, garantita da un processo coordinatore centralizzato e noto a tutti i client; Naturalmente la soluzione era poco “robusta”: il server costituiva un single point of failure ed in caso di guasti gli effetti erano: o condizioni di deadlock permanente conseguente al rifiuto delle richieste di accesso al database; o corruzione dei dati, a causa del loro aggiornamento contemporaneo da parte di più client (in caso di intervento manuale dell’utente, tasto reset); Purtroppo, entrambi gli aspetti vanificavano di fatto la validità dell’intero software…. Reti di calcolatori - 3 Scelte tecnologiche (1) Per quanto riguarda l’ambiente di sviluppo, si prevede di mantenere quello del software originario, ovvero Visual Studio 2005 e il linguaggio C#. Non sono sorti in fase progettuale motivi sufficienti per spingere lo sviluppatore alla realizzazione di un’applicazione ibrida. Tale decisione è stata giustificata anche dalla disponibilità, nell’ambiente scelto, di un valido strumento di supporto alla programmazione distribuita: Microsoft .NET Remoting Reti di calcolatori - 4 Scelte tecnologiche (2) Per le motivazioni riportate di seguito, la persistenza dei dati verrà garantita attraverso ADO .Net, Reti di calcolatori - 5 Progetto (1) Da un punto di vista macroscopico, l’architettura elaborata prevede l’adozione del modello three-tier introducendo una netta separazione tra i livelli di presentazione (i client), di business (la logica applicativa mantenuta dai server) e di persistenza dei dati (database server). In particolare, la matrice sottostante deriva dall’applicazione del patter PAC. Reti di calcolatori - 6 Progetto (2) Scalabilità e flessibilità sono stati i principi guida del processo di sviluppo: a tal fine è stato introdotto il DiscoveryServer per la pubblicazione dinamica dei servizi; La tolleranza ai guasti è garantita invece dall’adozione di un modello standby: si associa un componente detto Monitor ad ogni server, avente il compito di controllare periodicamente la liveness di quest’ultimo. In caso di guasto si provvede all’attivazione di una copia di standby del servizio, in maniera trasparente all’utente. Per l’accesso mutuamente esclusivo / concorrente / transazionale alla base dati, in fase progettuale si ritiene opportuno l’impiego di un database server commerciale, in quanto tali problematiche esulano dallo scopo del presente lavoro. Reti di calcolatori - 7 Progetto (3) L’architettura risultante è dunque la seguente: election Client Manager Standby copy Monitor election Discovery Server Price-List Manager Standby copy Monitor Client Manager Client Price-list manager Client DB Server Reti di calcolatori - 8 Prototipo (1) La realizzazione di un prototipo funzionante, nel rispetto dei vincoli temporali imposti, ha richiesto due compromessi: impiego della tecnologia ADO.Net per la persistenza dei dati: l’adozione di un database server avrebbe richiesto infatti tempi di apprendimento / sviluppo / deployment troppo lunghi, per cui l’autore ha preferito introdurre database Access locali ai server, gestiti attraverso ADO.Net. A questa decisione segue la necessità del mantenimento della coerenza tra i dati gestiti dai server primari e quelli mantenuti dalla copia di standby. implementazione completa dell’infrastruttura di supporto, ma integrazione di un unico servizio completamente funzionante a disposizione del cliente: sempre per motivi di tempo e per non dover adottare soluzioni temporanee per la sincronizzazione dell’accesso a dati condivisi (in vista dell’introduzione di un apposito database server), il prototipo prevede l’erogazione di un unico servizio, ovvero quello demandato alla gestione dei pazienti dello studio odontoiatrico. Reti di calcolatori - 9 Prototipo (2) Gestione della persistenza: ogni componente lavora sulla propria copia dei dati mediante un meccanismo basato su ADO .Net realizzato e testato da tempo dall’autore. In estrema sintesi, una libreria estende le funzionalità dei componenti propri del framework .Net consentendo non solo il mantenimento dell’associazione e della consistenza tra la base dati e il DataSet, ma anche la creazione automatica delle entità del business layer. Dal punto di vista prestazionale, a fronte di un transitorio iniziale dovuto al caricamento dell’intero database nel DataSet (residente in memoria principale) si ha un’estrema rapidità di esecuzione delle operazioni di aggiornamento dei dati, aspetto molto importante per la scelta delle politiche realizzate dal server. Si vuole sottolineare come il punto precedente richieda la specifica della sola linea di codice: StudioDocument _document = new StudioDocument(); Reti di calcolatori - 10 I componenti Pur delegando all’apposita relazione una descrizione dettagliata dei singoli componenti dell’architettura, si presenteranno in seguito: Le entità responsabili del soddisfacimento dei requisiti funzionali: i server; I client per l’interazione con l’utente; Il DiscoveryServer per la pubblicazione dinamica dei servizi; Monitor e StandbyManager per garantire liveness e fault tolerance del sistema. Reti di calcolatori - 11 Cliente vs servitore (1) Segue l’interfaccia del server Client_manager demandato alla gestione dei pazienti: Reti di calcolatori - 12 Cliente vs servitore (2) - Osservazioni I primi tre metodi permettono l’aggiornamento dei dati contenuti nel database e restituiscono all’utente un’indicazione della corretta esecuzione dell’operazione mendiante la classe Response; Get_client () consente il recupero di tutti i dati relativi ad un singolo paziente per la loro visualizzazione attraverso un’apposita form; Get_client_list() invece restituisce al client una lista di tutte e sole le informazioni necessarie al popolamento della ListView a fianco L’introduzione dei due diversi metodi risponde all’esigenza di ottimizzare la comunicazione tra i due componenti. Reti di calcolatori - 13 Cliente vs servitore (3) - Osservazioni La coppia di metodi di registrazione e deregistrazione consente l’implementazione di un meccanismo di call-back per segnalare ai client variazioni della lista dei pazienti: grazie alla serializzazione dei delegati offerta da .Net Remoting, ogni modifica al database si riflette nella generazione di un evento di tipo BasicEvent contenente sia un’indicazione dell’ azione, sia un’istanza della classe ThinyClient. I clienti registrati possono dunque aggiornare la propria lista, naturalmente attraverso un thread diverso da quello principale. Anche se il modello potrebbe sembrare troppo “accoppiato”, sono stati studiati diversi accorgimenti per limitare gli svantaggi di questa soluzione. Reti di calcolatori - 14 Cliente vs servitore (3) - Osservazioni Il metodo Print_client_report invia al server la richiesta di stampa di report relativi ad uno o più pazienti. La peculiarità di quest’operazione è l’attributo “OneWay()”: il processo di stampa è infatti computazionalmente oneroso e tipicamente richiede alcuni secondi. Possedendo un effetto visibile e potendo così discriminare facilmente sul successo o meno della richiesta, al metodo è stata associata una politica di tipo Fire&Forget : il client invoca l’operazione e prosegue nella computazione senza preoccuparsi di un eventuale valore di ritorno. Il server non si fa direttamente carico del servizio, ma lo delega ad un thread creato ex-novo. Il metodo Are_you_alive è connesso alla gestione della liveness del server, ed è presente nell’interfaccia al solo fine dichiarativo. Il server è registrato presso l’infrastruttura .Net Remoting come WellKnownObject in modalità Singleton. Per evitare problemi con eventuali firewall, si preferisce l’utilizzo del canale Http al Tcp; conseguentemente i messaggi di richiesta verranno inoltrati in formato Soap anzichè binario. Reti di calcolatori - 15 Server vs Monitor (1) – Check della liveness Il Monitor, unico per ogni server, è chiamato a svolgere tre compiti: pubblicare il servizio interagendo con il DiscoveryServer; inviare periodicamente messaggi di heartbeating per assicurarsi della vitalità del server controllato; in caso di mancata risposta, aggiornare l’entry del DiscoveryServer ed attivare la copia di standby, attraverso il suo controllore (StandbyManager). Il primo punto è espletato tramite l’invocazione del metodo Subscribe del DiscoveryServer. Il secondo mediante la creazione di un thread, responsabile dell’invio dei messaggi “Are you alive”. L’abilitazione della copia di standby invece avviene conseguentemente all’inoltro della richiesta di election al StandbyManager. Reti di calcolatori - 16 Server vs Monitor (2) – Osservazioni Lato server, per evitare di introdurre carico computazionale connesso a funzionalità che non riguardano la logica applicativa, è stato sviluppato un sink che filtra i messaggi in ingresso, costruisce la risposta alle richieste di liveness e l’invia al mittente senza interessare i livelli sovrastanti. Il sink (che di fatto realizza il pattern Interceptor) è posizionato immediatamente al di sopra del formatter in quanto lavora su messaggi e non stream di dati o envelope Soap. Lato client, è prevista una classe statica che ha il compito sia di gestire i servizi, attivando e rendendo disponibile a livello applicativo i remote refereces, sia di effettuare il recovery nel caso in cui si verifichino problemi di connessione con il server. Il funzionamento dell’intero sistema si basa sull’assunzione di affidabilità del mezzo trasmissivo, da cui risulta improbabile la perdita di messaggi. Si tratta di una supposizione verosimile in quanto l’ambiente di deployment sarà una piccola rete locale con non più di dieci client (come indicato dal committente). Reti di calcolatori - 17 DiscoveryServer Il componente è stato introdotto sia per una maggior scalabilità, sia per l’aggiornamento dinamico degli Url dei servizi (garantendo il recovery dei client). L’Url di un server è richiesto solo al primo accesso di un client. La lista dei servizi è implementata attraverso un’Hashtable formata dall’insieme delle coppie [NomeServizio,Url]. Non essendo prevista la persistenza dei dati, l’attivazione dev’esser necessariamente effettuata in modalità Singleton. Benché il prototipo preveda l’implementazione del solo Client_manager, rendendo equivalente l’impiego dei metodi Lookup_service() e Get_services(), l’utilizzo del secondo è consigliato per l’applicazione di release in quanto limita l’interazione tra i client ed il DiscoveryServer Reti di calcolatori - 18 StandbyManager e checkpointing (1) A causa delle peculiarità del prototipo, il compito di questo componente non è solo l’attivazione della copia di standby di un server, ma è anche responsabile dell’aggiornamento del database su cui essa opera, come si può notare dall’interfaccia a lato. Per il checkpointing è stato introdotto nel server primario un Updater_sink che provvede all’invocazione degli opportuni metodi: in maniera trasparente rispetto al livello applicativo; in modalità event-driven, dipendentemente dall’esito dell’operazione, analizzando il risultato dei livelli a monte. Reti di calcolatori - 19 StandbyManager e checkpointing (2) L’attributo [OneWay()] dei metodi dell’interfaccia esplica la politica ottimista adottata; seguono le motivazioni alla base di questa scelta: la fase di checkpointing è subordinata al successo dell’operazione sul server primario; un problema è segnalato all’utente ma non influenza il StandbyManager; la limitata complessità delle operazioni di aggiornamento del DataSet rende remota la possibilità che il manager sollevi eccezioni durante tale fase, a meno di un suo fallimento totale; il prototipo mira all’esposizione dell’intera architettura: sarebbero possibili numerosi accorgimenti per garantire l’allineamento delle copie, ma lo sforzo sarebbe vanificato dall’introduzione futura di un database server. Reti di calcolatori - 20 StandbyManager e copia del servizio (2) La classe Configurator si occupa dell’attivazione sia del StandbyManager, sia della copia secondaria, mediante un meccanismo ad eventi: La ricezione di un messaggio di election genera l’evento “Disable_server”; Il relativo event handler è registrato dal metodo Configurator.Init_manager() che contemporaneamente attiva anche canale e servizio remoto (il StandbyManager); In caso di elezione, l’event handler provvede alla disconnessione del manager dal canale di comunicazione e alla registrazione dinamica della copia di standby; Si sottolinea che il componente implementato può esser configurato sia come server primario, sia come copia secondaria, semplicemente agendo sul file di configurazione dell’applicazione (flag Is_primary_copy). Reti di calcolatori - 21 Conclusioni L’intero lavoro è stato svolto prestando particolare attenzione alla flessibilità e scalabilità del prodotto software: a tal fine, per ogni componente sono stati introdotti parametri di configurazione, modificabili dall’amministratore di sistema. Il prototipo, seppur limitato nei servizi, ha portato all’arricchimento dell’autore sotto diversi aspetti: sperimentazione delle problematiche affrontate nel corso di Reti d.C.; acquisizione di esperienza in merito a “nuove” tecnologie, come Microsoft .Net Remoting; confronto diretto con il cosiddetto “chatty vs. chunky trade-off”; definizione di un modello sufficientemente “robusto” per soddisfare le richieste del committente e contemporaneamente limitare gli interventi futuri del programmatore. Reti di calcolatori - 22 Sviluppi futuri Seguono alcune linee guida per evoluzioni future del prodotto (in ordine di priorità): introduzione di un database sever per il superamento delle limitazioni del prototipo; implementazione degli innumerevoli servizi previsti dall’applicazione originale e loro integrazione nell’architettura progettata; sviluppo di un LogServer per unificare la raccolta di informazioni in merito sia al funzionamento normale del sistema, sia all’occorrenza di guasti; utilizzo di nuovi interceptor e dei contesti per garantire l’identificazione degli utenti e la sicurezza dei loro accessi; realizzazione di canali di trasporto personalizzati per problematiche connesse alla privacy dei pazienti, adottando a tal scopo strumenti per crittografare i flussi di dati; perfezionamento del deployment, con avvio automatico in background dei vari server al boot del sistema operativo; Cessione (dietro compenso) del prodotto finale al committente. Reti di calcolatori - 23