Università degli studi di Bologna
Facoltà di Ingegneria
Corso di laurea Specialistica in
Ingegneria Informatica
Relazione di Reti di Calcolatori LS
“Reingegnerizzazione di un’applicazione per la
gestione di uno studio odontoiatrico: da un
modello single-user ad un’architettura distribuita
tollerante ai guasti basata sul discovery dinamico
dei servizi”
Relazione di
Gottardi Gianpaolo
Anno Accademico 2006-2007
Indice
Introduzione ........................................................................................................................... pag. 3
Capitolo 1 – L’applicazione originale.................................................................................... pag. 4
Capitolo 2 – Microsoft .Net Remoting ................................................................................... pag. 6
Capitolo 3 – L’architettura
a - Il progetto............................................................................................................... pag. 8
b - Pattern PAC ........................................................................................................... pag. 8
c - Discovery dinamico dei servizi .............................................................................. pag. 10
d - Tolleranza ai guasti .............................................................................................. pag. 11
e - Il prototipo ............................................................................................................ pag. 12
Capitolo 4 – I componenti
a - Condivisione dei metadati ..................................................................................... pag. 15
b - Cliente e servitore: l’interfaccia del remote object............................................... pag. 15
c - Cliente e servitore: attivazione .............................................................................. pag. 19
d - Cliente e servitore: configurazione ....................................................................... pag. 20
e - Server e Monitor: pubblicazione e check della liveness del servizio .................. pag. 21
f - DiscoveryServer: pubblicazione dei servizi .......................................................... pag. 22
g - StandbyManager e relativa copia del servizio ...................................................... pag. 23
Capitolo 5 – Conclusioni e sviluppi futuri ............................................................................. pag. 26
Bibliografia ............................................................................................................................. pag. 27
2
Introduzione
Il progetto alla base di questa relazione riguarda la realizzazione di un’infrastruttura in grado di
estendere il dominio di funzionamento di un’applicazione software preesistente per la gestione di
uno studio odontoiatrico, la cui descrizione è demandata al Cap. 1.
Esulando dalle motivazioni di carattere strettamente accademico, il lavoro ha una giustificazione
reale, nata dalla collaborazione dell’autore con alcuni studi odontoiatrici dell’hinterland bolognese:
uno dei committenti ha infatti esplicitamente richiesto l’ampliamento delle funzionalità
dell’applicazione monoutente originale per rendere possibile l’accesso ai diversi servizi da più
postazioni mobili, nell’ambito di una piccola rete locale. Per approfondire le tematiche affrontate
nel corso di Reti di Calcolatori LS, sperimentarle concretamente e nello stesso tempo offrire al
cliente una soluzione qualitativamente superiore, nella progettazione del sistema sono stati
introdotti meccanismi per consentire il discovery dinamico dei servizi e per garantire agli stessi
continuità in caso di guasto.
L’architettura introdotta verrà illustrata nel terzo capitolo, posticipando la descrizione dettagliata dei
singoli componenti all’analisi della tecnologia adottata, al fine di garantire una maggiore
comprensione dei dettagli implementativi.
La realizzazione prototipale è comunque omologa a quella dell’applicazione originale, ovvero si
mantiene come linguaggio Microsoft C# e come ambiente di sviluppo MS Visual Studio 2005.
Le motivazioni di questa scelta si devono ricercare sia nel costo (in termini di tempo)
dell’apprendimento di altre tecnologie (e.g. CORBA) e dell’eventuale migrazione di componenti
già testati e sviluppati, sia nell’ ampio corredo di librerie per il supporto alla programmazione
distribuita, messo a disposizione dall’architettura MS .Net Remoting. Una sua breve descrizione
verrà fornita nel secondo capitolo.
3
Capitolo 1 – L’applicazione originale
La collaborazione dello studente con alcuni studi
odontoiatrici ha portato alla realizzazione di un’applicazione
in grado di supportare i committenti nella maggior parte degli
aspetti logistici connessi alla loro attività.
In particolare sono stati sviluppati diversi moduli, demandati
alla gestione di:
 Pazienti dello studio (i clienti);
 Listini dei servizi erogabili;
 Cartelle cliniche contenenti lo storico delle
prestazioni mediche relative ad ogni cliente;
 Anamnesi del paziente;
 Fatture ed acconti per consentire anche la gestione
fiscale;
 Preventivi;
 Appuntamenti e richiami;
Tutti gli elementi sopra riportati contemplano la possibilità di
stampare la relativa documentazione nonché materiale di
carattere legislativo (e.g. D.L. 626, legge sulla privacy).
Da ultimo è stato realizzato l’interprete di un linguaggio
attraverso il quale l’utente finale è in grado di creare, compilare e stampare autonomamente
documenti, svincolando da questo aspetto l’autore e consentendo un buon grado di
personalizzazione.
Dal punto di vista tecnologico, l’applicazione è stata realizzata in linguaggio Microsoft C#,
utilizzando l’ambiente MS Visual Studio 2005. L’interazione con l’utente è totalmente gestita
attraverso finestre di dialogo in cui sia la veste grafica sia alcuni meccanismi di supporto (e.g. i
ToolTips, labels associate ad ogni controllo visibile) sono pensati per informare e guidare
l’utilizzatore nelle proprie scelte.
La gestione dei report si è avvalsa dell’ engine Crystal, dell’omonima società, mentre
l’implementazione del modello entity-relationship dei dati è stata effettuata attraverso ADO .Net: il
modello disconnesso che questa tecnologia sussume, consente l’accesso ai dati, il loro salvataggio
in una cache locale (il DataSet) e la chiusura della connessione con un database, evitando
un’interazione permanente con quest’ultimo. L’utente in questo modo può lavorare sul DataSet,
interagendo con il supporto di memorizzazione solo in caso di effettiva
necessità (e.g. aggiornamento). Il costo del transitorio iniziale dovuto
al caricamento dei dati e al conseguente popolamento delle tabelle
(DataTable) è ampiamente ripagato dalla velocità di esecuzione delle
operazioni di accesso e aggiornamento.
In merito alla persistenza dei dati, i file Xml introdotti inizialmente
sono stati abbandonati a favore di un database Access che risulta
alquanto più “ingombrante” ma consente la definizione di politiche
minime di sicurezza.
Il modello di esecuzione della prima versione del prodotto era strettamente monoutente. Per
soddisfare le richieste dei clienti in merito alla possibilità di accedere contemporaneamente ai
servizi da più postazioni mobili, è stato sviluppato un primo prototipo basato sulla condivisione del
4
database e sull’impiego di un processo coordinatore centralizzato noto a tutti i client, in grado di
garantire la mutua esclusione degli accessi. La soluzione era estremamente semplice e prona ad
errori: il server costituiva infatti un single point of failure ed in caso di problemi, si potevano avere
condizioni di deadlock, al meglio, o corruzione dei dati vanificando in pratica l’utilità dell’intero
software.
Per questo è risultato indispensabile il progetto di una nuova architettura, in grado di superare il
modello two-tier delle applicazioni client/server a favore di quello three-tier (figura sottostante) che
garantisce una maggior scalabilità, flessibilità e manutenibilità dei servizi, introducendo una netta
separazione tra i livelli di presentazione (i client), di business (la logica applicativa mantenuta dai
server) e di persistenza dei dati.
Benché il progetto contempli questa distinzione, per motivi legati al ridotto tempo a disposizione
del programmatore, i server manterranno localmente copie dei dati di loro interesse, demandando a
sviluppi futuri l’introduzione di un database server per la gestione della concorrenza / mutua
esclusione / transazionalità degli accessi ai dati.
5
Capitolo 2 – Microsoft .Net Remoting
a – Un po’ di storia
.Net Remoting è un’application programming interface (API) per la comunicazione tra processi
rilasciata dalla Microsoft nel 2002 con la prima versione del framework .Net. Rientra nel filone
delle tecnologie introdotte dal colosso mondiale a partire dal 1990 con la prima versione dell’
Object Linking and Embedding (OLE), seguito dal Component Object Model (COM) nel 1993,
Distributed Component Object Model (DCOM) nel 1997 e poi rinominato ActiveX.
Benché la versione 2.0 del framework sia stata resa pubblica da poco più di due anni, la 3.0 è già
stata rilasciata come componente integrato nel sistema operativo Windows Vista e tra le novità
annovera il passaggio da .Net Remoting a Windows Communication Foundation (WCF).
b – Architettura di .Net Remoting
L'infrastruttura di .Net Remoting rappresenta un approccio astratto alla comunicazione
interprocesso e definisce la maggior parte degli elementi comuni a tutti i middleware attualmente a
disposizione di un programmatore per la realizzazione di applicazioni distribuite.
La descrizione di questi elementi esula dalla presente relazione, ma si ritiene opportuno effettuare
una comparazione tra le caratteristiche di .Net Remoting e quelle di altri strumenti come CORBA,
COM/DCOM e RMI:
 semplicità di implementazione: un’applicazione remota sviluppata mediante .Net consente
di focalizzare l’attenzione sul dominio applicativo, agevolando notevolmente la gestione dei
canali di comunicazione, dei formati per la codifica e dei riferimenti remoti. E’ ad esempio
possibile passare da un canale Tcp di comunicazione (codifica binaria dei messaggi, velocità
maggiore) ad uno Http (utilizzo di SOAP per l’incapsulamento delle richieste, minor
ottimizzazione ma maggior interoperabilità) semplicemente modificando una linea di un file
di configurazione. Rispetto a Java RMI non è necessaria la generazione manuale di stub e
skeleton, né la definizione delle interfacce in un linguaggio astratto (IDL) come in CORBA
e DCOM. Non viene inoltre imposto nessun vincolo di piattaforma o linguaggio come
accade per gli Enterprise Java Beans o per DCOM. Uno dei pregi dell’infrastruttura
riconosciuto dall’autore consiste nel fatto che a queste agevolazioni non corrisponde alcun
vincolo implementativo: il programmatore può infatti decidere il livello di complessità dei
singoli componenti; ad esempio l’attivazione di un servizio remoto può richiedere la sola
scrittura di due righe di codice oppure il progetto di un protocollo di trasferimento
personalizzato.
 estendibilità dell’architettura: .Net Remoting offre allo
sviluppatore una vasta gamma di personalizzazioni
della stessa architettura. Il suo funzionamento di
default prevede le seguenti azioni: ogni volta che un
client mantiene un riferimento ad un oggetto remoto,
viene creato un transparent proxy (analogo allo stub in
RMI) che maschera l’oggetto riferito e permette
l’invocazione dei suoi metodi, attraverso la
conversione della chiamata in un messaggio. Questo
viene passato attraverso un numero variabile di livelli e
sottoposto infine a serializzazione, ovvero alla sua
6



trasformazione in uno specifico formato per il trasferimento, ad esempio SOAP, per poter
successivamente esser inviato al processo remoto mediante un canale / protocollo di
trasporto (Http o Tcp). Lato server, il messaggio viene deserializzato ripercorrendo in
maniera inversa i layer, fino al raggiungimento del dispatcher che invoca il metodo richiesto
e (se la chiamata lo prevede) elabora un messaggio di risposta che verrà inviato con la stessa
modalità al client. Diversamente dalle altre architetture, la maggior parte dei layers in .Net
può esser estesa o rimossa e nuovi livelli possono esser introdotti per personalizzare la
modalità con cui sono processati i messaggi. Il progetto esposto in seguito, ad esempio,
sfrutta questa possibilità per ottimizzare l’interazione di un server con il proprio monitor. Si
ritiene opportuno notare che l’aggiunta/rimozione di nuovi livelli (detti anche sink) può
esser eseguita dall’amministratore del sistema, modificando il file di configurazione del
server, senza richiedere la modifica del codice sorgente.
definizione delle interfacce: molti middleware tra cui DCE/RPC, RMI, J2EE richiedono la
generazione manuale di stub e skeleton, che nascondono (uno lato client,l’altro lato server) a
livello applicativo la natura distribuita dell’oggetto cui si riferiscono. La maggior parte di
questi ambienti richiede la definizione dell’interfaccia in un opportuno Interface Definition
Language successivamente sottoposta a precompilazione per la generazione quantomeno
degli header di stub e skeleton in uno specifico linguaggio di programmazione. .Net
Remoting al contrario utilizza un proxy generico per tutti gli oggetti remoti, recuperandone
le caratteristiche grazie al meccanismo della reflection. Non per questo viene meno
l’esigenza di separare l’interfaccia dall’implementazione; la tecnica utilizzata anche in fase
di realizzazione del prototipo di questo progetto è la definizione dell’interfaccia del servizio
in una libreria separata da quella contenente l’implementazione, rendendola
successivamente disponibile ai client.
serializzazione dei dati: analogamente a quanto accade in CORBA e in Java RMI, il
framework .Net si occupa del passaggio dei dati per copia tra cliente e servitore; se tutti i tipi
primitivi sono serializzabili, quelli complessi richiedono semplicemente la specifica
dell’attributo di classe [Serializable] o l’implementazione dell’interfaccia ISerializable. I
tipi di dati primitivi che compongono l’oggetto o eventuali “sotto-oggetti” (i quali devono
esser serializzabili a loro volta) verranno automaticamente codificati ed inviati al server,
eventualmente in formato Xml per superare possibili eterogeneità di piattaforma.
Lifetime management: come appreso dal corso di Reti di Calcolatori, la gestione del ciclo di
vita dei componenti di un’applicazione distribuita può esser oggetto di diverse politiche:
storicamente, il primo approccio prevedeva una connessione Tcp diretta tra cliente e
servitore: una volta terminata le risorse di quest’ultimo potevano esser deallocate. Un
modello più evoluto è quello adottato da DCOM che combina reference counting a un
meccanismo di pinging: se un client non risponde ad un ping viene considerato non più
raggiungibile e si decrementa il reference counter. Una volta che questo ha raggiunto lo
zero, le risorse sono liberate. La validità di questi modello è stata vanificata nel tempo dal
progressivo aumento dell’eterogeneità delle reti: tralasciando l’approccio più datato, la
presenza di firewall, router o gateway (spesso configurati in maniera diversa e soprattutto da
amministratori diversi) rende difficile la comunicazione tra componenti distribuiti e
conseguentemente l’adozione di un modello unificato per la gestione del lifetime. Per
esperienza diretta, è possibile perdere una giornata intera a configurare un server senza alcun
risultato, perché il client possiede più servizi che gestiscono le porte di comunicazione. .Net
Remoting di default assegna un tempo di vita ad ogni oggetto remoto, incrementato ad ogni
chiamata di un suo metodo. Questo comportamento può esser ampiamente personalizzato
modificando il time to live o registrando, ad esempio, presso il server un componente
chiamato sponsor che viene contattato nel caso in cui sia scaduto il tempo di vita
dell’oggetto, consentendo una sua estensione attraverso il rinnovo del lease, oggetto che
incapsula un valore di TimeSpan, gestito da un LeaseManager.
7
Capitolo 3 – L’architettura
a – Il progetto
Come anticipato nei capitoli precedenti, le linee guida del progetto sono le seguenti:
 estensione del paradigma di esecuzione dell’applicazione originale, introducendo un
modello three tier distribuito;
 discovery dinamico dei servizi.
 introduzione di meccanismi per garantire tolleranza ai guasti;
b – Pattern PAC
Per quanto riguarda il primo punto, la separazione dei diversi layer è stata eseguita applicando il
pattern PAC, Presentation, Abstraction, Control. Il PAC rientra nell'insieme dei pattern
architetturali ed è uno dei due dedicati ai sistemi interattivi i quali hanno come scopo la
suddivisione coerente del sistema in parti, in modo da garantire allo stesso tempo espandibilità,
riusabilità ed interoperabilità, come descritto in [POSA]. In particolare si propone una soluzione
"a matrice" in cui si considerano a livello verticale i macrosistemi secondo una suddivisione
three-tier (Client, Server e DbServer). I piani orizzontali evidenziano invece, all'interno del
medesimo macrosistema, la suddivisione PCE delle entità componenti.
Client: si prevede di mantenere il livello di presentazione dell’applicazione originale, mentre quello
di business dev’esser completamente rielaborato per consentire l’interazione con i server remoti.
Quest’ultima si prevede avvenga non solo in modalità sincrona con sospensione del client in attesa
della risposta dal server, ma anche in modalità asincrona, sia di tipo Fire&Forget sia
ResultCallback. Si rimanda una sua analisi ai capitoli successivi.
I client sono costituiti dai dispositivi mobili a disposizione del personale medico e rappresentano sia
dispositivi di input che di output, permettendo sia la visualizzazione, sia l’inserimento/modifica
8
delle informazioni gestite dal database remoto attraverso apposite form (i client saranno quindi
WindowsApplications).
Server: sono responsabili della business logic dell’intero progetto. Nel caso specifico si prevede
l’introduzione di un numero di server pari alle diverse aree applicative (gestione pazienti, listini,
prestazioni mediche, appuntamenti etc.) più quelli di supporto (discovery, monitor, manager copia
di standby). Naturalmente devono esser realizzati ex novo e, non essendo caratterizzati da un livello
di presentazione consistente, possono concretizzarsi in ConsoleApplications). La tipologia dei
servizi offerti e la delega ad estensioni future dell’implementazione di meccanismi per la sicurezza
(correlata al concetto di sessione) fa protendere il progettista verso l’introduzione di server stateless.
In realtà, si potrebbero contemplare code per la memorizzazione temporanea dei messaggi di
richiesta, in grado di sopperire a condizioni di stress del server, di cui rappresenterebbero lo stato.
Tuttavia, il limitato numero di client (il committente lo indica non superiore a 8) , l’impiego di
funzioni one-way per le richieste più onerose e di ADO.Net (molto performante nell’accesso ai dati)
rende remota la possibilità di denial of service dovuto a sovraccarico del servitore.
Per questo la scelta tra i seguenti modelli di attivazione (offerti dalle API di .Net Remoting)
riguarda altre problematiche:
 Modello “Singleton”: la prima chiamata al servitore provoca l’attivazione di una sua unica
istanza che si occuperà da quel momento in poi della gestione di tutte le richieste anche da
parte di clienti diversi. L’overhead complessivo è limitato ed è possibile mantenere stato
tra le diverse chiamate. Per contro, l’esecuzione del specifico servizio è sequenziale. La
figura sottostante mostra come un oggetto remoto configurato in Singleton mode serva
richieste di clienti diversi. Naturalmente è necessario prestare attenzione alla tempo di vita
del Well-Known object, la cui scadenza comporterebbe una deallocazione automatica della
risorsa da parte dell’infrastruttura per cui richieste successive potrebbero esser servite da
istanze diverse.

Modello “Single Call”: l’infrastruttura crea ad ogni richiesta da parte dei clienti una nuova
istanza dell’oggetto servitore; una volta terminata l’esecuzione è resa disponibile per la
deallocazione. A fronte del costo di attivazione (che può comportare un degrado eccessivo
delle prestazioni in caso di forte interazione con i client) e del paradigma strettamente
stateless, l’esecuzione lato server può esser concorrente. In tal caso sono comunque
necessari meccanismi di sincronizzazione per l’eventuale accesso a risorse condivise.
9
Si intende sottolineare che la distinzione tra i due modelli si concretizza a livello di codice in un
unico parametro:
Singleton activation:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof( SomeMBRType ), "SomeURI",
WellKnownObjectMode.Singleton );
Single call activation:
RemotingConfiguration.RegisterWellKnownServiceType(
typeof( SomeMBRType ), "SomeURI",
WellKnownObjectMode.SingleCall );
L’espressione “WellKnownObject” si riferisce al fatto che l’applicazione server pubblica l’oggetto
remoto ad uno specifico indirizzo/endpoint ed è responsabile della sua attivazione; si tratta di un
cosiddetto oggetto “Server Activated”, in contrapposizione a quelli “Client Activated” caratterizzati
da un URI diverso (e quindi un’istanza diversa) per ogni copia mantenuta ed attivata dall’utente sul
server.
Database manager / server: gestisce la persistenza dei dati e fornisce servizi per l’accesso
concorrente / mutuamente esclusivo / transazionale agli stessi. Data la complessità della
realizzazione di un componente simile, in fase progettuale si prevede l’adozione di uno strumento
commerciale come SqlServer , MySql o Oracle Db Server. Le considerazioni effettuate in merito a
questo componente nella sezione dedicata all’analisi del prototipo comporteranno sostanziali
modifiche alla struttura e al modello di interazione tra i diversi layer.
c – Discovery dinamico dei servizi
Anziché cablare l’Universal Resource Locator (URL) dei server nei file di configurazione di ogni
client, per garantire una maggior scalabilità dell’architettura si prevede l’introduzione di un server
di discovery che consente la registrazione dinamica degli URL dei servitori e lookup per il loro
reperimento. Dal punto di vista della tolleranza ai guasti, questo componente rappresenta un single
point of failure: un suo guasto potrebbe inficiare il funzionamento dell’ intero sistema. Vista la
semplicità delle funzioni che è chiamato a svolgere e per il principio di minima intrusione non si
prevede di adottare in tal senso alcun accorgimento, quale potrebbe esser l’introduzione di
replicazione o di un supporto per la condivisione dei dati (e.g. uno spazio di tuple).
La lista dei servizi è labile, ovvero mantenuta solo per il tempo di funzionamento del
DiscoveryServer: allo spegnimento le informazioni sono perse e alla sua riattivazione, si deve
procedere ad una nuova registrazione dei server.
10
d – Tolleranza ai guasti
Al fine di garantire un livello minimo di recoverability del sistema a fronte di crash dei servitori, si
associa ad ognuno di essi un Monitor che ne verifica periodicamente la vitalità attraverso messaggi
di HeartBeat. Se il controllore non ottiene una risposta in tempo utile, provvede all’attivazione di
una copia secondaria del server (il cui deployment è statico e a carico dell’amministratore) situata
(possibilmente) su una macchina diversa e al conseguente aggiornamento del DiscoveryServer.
L’interruzione del servizio è relativa al solo tempo necessario al recupero del nuovo URL da parte
del client. In prima battuta, essendo i server stateless non è necessario effettuare alcun
aggiornamento della copia secondaria. I concetti di hot / cold copy non possono esser quindi
applicati, nemmeno per quanto riguarda l’attivazione perché quest’ultima è comunque di
competenza dell’infrastruttura .Net e la semplice dichiarazione del servizio non ha alcun effetto
sullo stesso.
I modelli di guasto previsti dal progettista sono i seguenti:

crash del cliente: non si intraprende alcuna azione correttiva; se era stata effettuata una
registrazione per la segnalazione di eventi, il cliente è eliminato dalla lista (per una maggior
comprensione si rimanda ai paragrafi successivi);

crash del servitore: attivazione di una copia di standby. La nuova locazione del servizio
viene recuperata in maniera trasparente all’utente;

crash del monitor: il single point of failure costituito dal monitor viene trattato nella stessa
maniera del DiscoveryServer; il basso carico computazionale da cui è interessato non induce
il progettista alla replicazione del componente. L’ipotesi di guasto singolo permette di
considerare remota la possibilità di failure contemporaneo del monitor e del server primario,
consentendo la segnalazione del problema e l’intervento dell’amministratore per il ripristino
del sistema.

crash del DiscoveryServer: gli effetti del guasto sono diversi, dipendentemente
dall’ottenimento o meno della lista dei servizi da parte del client. In caso affermativo, il
guasto del componente non consente l’aggiornamento dell’URL da parte del monitor (che
ha rilevato il crash del server primario) e si potrebbe avere un interruzione del servizio.
L’ipotesi di guasto singolo anche in questo caso permette di supporre la tempestiva
segnalazione del problema ed intervento dell’amministratore. In caso negativo l’interruzione
del servizio è inevitabile, a meno che l’utente non conosca autonomamente l’indirizzo del
servitore e lo immetta manualmente nel file di configurazione (soluzione poco elegante, ma
efficace).

crash del gestore della copia secondaria: si provvede alla segnalazione del problema e al suo
recovery. La remota possibilità di crash contemporanei del server primario e del gestore
della copia di standby potrebbe a prima vista portare ad una situazione di inconsistenza dei
dati, le cui copie locali (introdotte per i motivi esposti nel seguente paragrafo) potrebbero
esser diverse. In questo caso sarebbe necessario analizzare il timestamp associato alle ultime
modifiche occorse, per individuare la copia più recente (e corretta, grazie al meccanismo di
propagazione degli aggiornamenti – v. dopo). Quest’ultima osservazione è applicabile anche
in caso di network partition, a cui il sistema reagirebbe in maniera errata, promuovendo la
copia di standby.
11
Complessivamente, l’architettura proposta è quella sottostante:
election
Client Manager
Standby copy
Monitor
election
Discovery
Server
Price-List
Manager
Standby copy
Monitor
Client
Manager
Client
Price-list
manager
Client
DB
Server
e – Il prototipo
La realizzazione di un prototipo funzionante, nel rispetto dei vincoli temporali sia accademici che
personali, ha portato a due compromessi:

impiego della tecnologia dell’applicazione originale per la persistenza dei dati: l’adozione di
un database server avrebbe richiesto 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 però l’apertura di nuovi scenari connessi al
mantenimento della coerenza tra i dati gestiti dai server primari e quelli mantenuti dalla
copia di standby. La soluzione adottata prevede un aggiornamento event driven di
quest’ultima da parte del servitore cui si riferisce (sempre che l’operazione sia stata da lui
eseguita con successo), in modalità trasparente rispetto all’ application layer. Compito
dell’amministratore sarà, oltre a scegliere la macchina su cui “attivare” la copia di standby
(o meglio, il suo manager), anche modificare il file di configurazione del relativo server.
L’insieme di dati su cui entrambi operano deriva dallo scorporo del modello entityrelationship previsto per l’applicazione originale. Si vuole anticipare una particolarità del
processo di implementazione dell’architettura: tutti gli elementi sono stati pensati per ridurre
al massimo l’intervento del progettista / programmatore, cercando di massimizzare la
flessibilità del prodotto; a tale scopo, i file di configurazione sia dell’applicazione sia
dell’infrastruttura remota hanno trovato largo impiego. In particolare uno stesso componente
(detto StandbyServer) è in grado di fungere da gestore di una copia secondaria o da server
primario (e di passare naturalmente dal primo al secondo in risposta ad un comando di
election) semplicemente agendo su un flag booleano.

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
12
l’erogazione di un unico servizio, ovvero quello demandato alla gestione dei pazienti dello
studio odontoiatrico. Non per questo si deve considerare parziale la soluzione proposta: si
tratta solo di un mezzo per descrivere le problematiche affrontate e i modelli di interazione
introdotti. Dalla loro estensione, ulteriori servizi possono esser facilmente integrati
nell’infrastruttura, richiedendo tempi abbastanza contenuti.
Persistenza dei dati:
Client Manager
Standby copy
Client
Manager
DataSet
DataSet
Price-List
Manager
Standby copy
Price-list
manager
DataSet
DataSet
Ogni componente gestisce la propria copia dei dati mediante un meccanismo basato su ADO .Net
realizzato e testato da tempo dall’autore: in estrema sintesi, una libreria appositamente sviluppata
(RootDataProject) 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; in tal modo lo sviluppatore può
concentrare la propria attenzione solo su quest’ultimo aspetto. Per fornire un esempio concreto, la
necessità di gestire i pazienti dello studio odontotecnico prevede per il Data Layer:

la specializzazione della classe RootDataTable attraverso ClientiDT e la sua dichiarazione
nel DataSet;

la specializzazione della classe RootDataRow attraverso ClienteRow per la definizione delle
proprietà specifiche dell’oggetto e la loro associazione (mediante un’estensione di
RootDataRowSchema) ai campi del supporto di memorizzazione (righe di una tabella di un
database o nodi di un documento Xml).
Il Business Layer prevede la sola specializzazione della classe ClienteRow per render possibile la
creazione automatica di un istanza di Cliente, il popolamento di suoi attributi e la propagazione
automatica nel database delle modifiche.
Opportuni DataAdapter permettono l’impiego di diversi supporti per la memorizzazione (file Xml,
database Access e altro) e i ConnectionManagers si occupano della gestione della connessione,
evitando che il programmatore debba scrivere manualmente tutte le relative istruzioni Sql.
Un Document si occupa della corretta inizializzazione di tutte le entità in gioco e del popolamento
del DataSet cui si riferisce. Un componente, chiamato DataLoader, implementa il pattern Singleton
e rappresenta un punto unico per l’accesso/aggiornamento dei dati. Come si potrà notare nei capitoli
13
successivi, il caricamento dei dati dal database (la cui locazione è un parametro configurabile)
richiede la specifica della seguente linea di codice:
StudioDocument _document = new StudioDocument();
Segue, a titolo esemplificativo, un diagramma delle classi dei componenti fondamentali
dell’infrastruttura:
Framework
.Net
Libreria universale di
supporto
all’infrastruttura
elaborata dall’autore
Elementi specifici del
modello E-R di
un’ applicazione
14
Capitolo 4 – I componenti
a – Condivisione dei metadati
Prima di passare all’analisi dei singoli componenti, è doveroso descrivere il metodo adottato per la
condivisione delle informazioni tra client e server, riguardanti le entità remote: .Net Remoting non
richiede che le interfacce siano definite attraverso un Interface Definition Language. Inoltre Proxy e
Stub sono generati dall’infrastruttura. Come render consapevole il client delle operazioni che può
richiamare sull’oggetto remoto? Il supporto consente la scelta tra quattro modalità principali:

Shared Implementation: gli assemblies che contengono l’implementazione dei server
objects sono condivisi con il cliente; è la soluzione che richiede il minimo sforzo, ma anche
quella più pericolosa: oltre ad esser teoricamente scorretta in quanto viola i principi di base
della programmazione distribuita, clienti maliziosi potrebbero disassemblare il codice
(usando, ad esempio, l’ Intermediate Language Disassembler – ILDASM - fornito con la
versione SDK del framework) ed accedere alla business logic del server;

Shared Interfaces: l’assembly condiviso contiene unicamente le interfacce implementate dai
servizi remoti (ed eventualmente classi di supporto); l’unico difetto di questa modalità è il
mancato supporto ad applicazioni avanzate che richiedono il passaggio degli oggetti remoti
come parametri di funzioni appartenenti ad un contesto diverso (ma non è questo il caso…);

Shared Base Classes: risolve il problema esposto poco sopra, in quanto l’impiego di classi
astratte anziché di interfacce permette il cast dinamico degli oggetti remoti che possono
quindi esser passati tra domini applicativi diversi;

Utilizzo di SOAP-SUDS: si tratta di programma introdotto recentemente in grado di estrarre
i metadati (ad esempio una type definition) da un server, avviato o meno. A partire dalle
informazioni recuperate crea un nuovo assembly a disposizione del client. L’utilizzo di
quest’applicazione elimina l’intervento del programmatore nella descrizione delle
informazioni condivise, ma richiede una sua completa conoscenza.
La modalità impiegata nell’implementazione del prototipo prevede la condivisione delle interfacce
e del conseguente assembly (e.g. libreria Shared_data per il server).
b – Cliente e servitore: l’interfaccia del remote object.
La descrizione dei due componenti principali parte dall’analisi dei servizi esposti dal server
ClientManager, (dove per client si intende un paziente) , definiti da un’apposita interfaccia
IClientManager; oltre a quelli “classici” di inserimento / modifica / rimozione dei dati di un
paziente, sono presenti:

metodi per la registrazione / deregistrazione di un “EventWrapper”;

un metodo per la stampa di un report relativo ad uno o più pazienti;

un metodo dedicato all’Heartbeating
15
public interface IClient_manager
{
Response add_client(string surname, string name,…, string e_mail);
Response remove_client(int client_code);
Response modify_client(int client_code, string surname, string name, … ,
string e_mail);
Fully_defined_client get_client(int client_code);
Thiny_client[] get_client_list();
void Register_for_events(Event_wrapper ew);
void Unregister_from_events(Event_wrapper ew);
[OneWay()]
void Print_client_report(bool single_client, int code);
bool Are_you_alive();
}
I requisiti funzionali del server sono espletati dai primi cinque metodi. L’inserimento di un paziente
(attraverso la specifica delle sue proprietà), la modifica e la rimozione si riflettono sui dati del
database; per contemplare situazioni di incoerenza (rimozione di un paziente non esistente,
duplicazione delle informazioni) al cliente è restituita un’indicazione dell’avvenuta esecuzione del
metodo attraverso la classe condivisa e serializzabile Response. Essa mantiene un enumerativo che
indica il successo (HRESULT.OK) o meno dell’operazione e nel secondo caso un messaggio di errore.
In questo modo il paziente può eseguire azioni correttive in quanto la segnalazione si riferisce
solamente a problemi derivanti dai dati inseriti dall’utente e non da guasti di componenti.
I metodi per il recupero di informazioni riguardanti uno o più pazienti, come si può notare, hanno
valori di ritorno diversi, per motivi connessi alla performance del servizio: difficilmente il cliente è
interessato all’intera lista (che consta solitamente di centinaia di nominativi), per cui risulterebbe
inutile inviare la totalità delle proprietà di un paziente (dodici campi testuali, uno numerico ed un
DateTime). Per questo la classe Thiny_client definisce solo le caratteristiche minime (Nome,
Cognome, Codice identificativo e Data di nascita, come concordato con il committente) necessarie
alla sola visualizzazione di una lista lato client. La classe Fully_defined_client (le cui istanze sono
costruite dinamicamente ad ogni richiesta) risponde tuttavia all’esigenza opposta per la
visualizzazione di tutte le proprietà di un paziente attraverso un’apposita form del client.
Meritano un discorso più articolato i due metodi Register_for_events e Unregister_from_events:
per mantenere allineati clienti e servitore si adotta un modello push di quest’ultimo, basato sulla
propagazione di eventi. Ad ogni modifica al database consegue la generazione di un BasicEvent che
mantiene informazioni sia sull’azione effettuata (enumerativo Action), sia l’elemento da essa
interessato (un ThinyClient). In questo modo si risparmia un eventuale round trip per il recupero dei
cambiamenti occorsi. Il meccanismo è supportato da .Net Remoting grazie alla serializzazione dei
delegati. Le implicazioni di questa soluzione sono diverse:

passaggio dall’unidirezionalità del rapporto tra cliente e servitore alla bidirezionalità. Questo
rende necessaria l’attivazione di un end-point di ricezione anche lato client, risolta
direttamente a livello di configurazione. Il canale di comunicazione dev’essere naturalmente
omogeneo a quello del server, mentre la scelta della porta non è di particolare importanza,
per cui si pone l’infrastruttura in attesa di una connessione su una qualsiasi delle porte
disponibili (specificando il valore 0 nel costruttore dell’ HttpChannel). Poco avanti sarà
possibile ritrovare questi elementi, nella breve analisi del file di configurazione del client.

introduzione di un legame tra le controparti, il quale potrebbe esser giudicato troppo
vincolante: in realtà sono state adottate misure per ridurre al massimo i problemi derivanti
16
dalla necessità di compresenza tra le entità, del modello. Lato server la generazione degli
eventi avviene recuperando la lista dei client che si sono registrati ed iterando sulla stessa
per l’invocazione asincrona del relativo delegato. In caso di suo insuccesso (e.g. a causa del
crash del cliente) l’esecuzione del server può continuare, previa deregistrazione del specifico
handler. Il metodo BeginInvoke consente l’invocazione asincrona di metodi sincroni: il
thread originale che ha inviato la richiesta può continuare l'esecuzione parallelamente al
metodo di destinazione, che viene eseguito in un thread di un pool.
Lato client, prima di eseguire qualsiasi accesso ai servizi remoti il cliente provvede alla sua
registrazione: se questa non ha successo, probabilmente è occorso un guasto ed è necessario
attivare la procedura di recovery. In fase di chiusura della finestra demandata all’interazione
con un servitore si procede invece alla deregistrazione del cliente: eventuali problemi non
sono segnalati proprio per la semantica dell’azione dell’utente. Il corretto funzionamento
dell’intero meccanismo, richiederebbe la conoscenza dell’handler specifico di ogni cliente,
da parte del servitore, portando ad una cosiddetta architettura server-to-server. Come si nota
dalla signature dei metodi di registrazione / deregistrazione lo sviluppatore ha invece
introdotto la classe (condivisa e serializzabile) EventWrapper che implementa il pattern
Proxy e funge da ripetitore dell’evento, evitando il suddetto inconveniente.
Alternativamente alla politica di CallBack cui è riconducibile l’intero meccanismo, sarebbe
stato possibile introdurre funzioni di Polling del servitore, aumentando però
considerevolmente il traffico tra le entità. Segue il diagramma di sequenza relativo
all’interazione “ad eventi” tra cliente e servitore.
17
Lato client, alla ricezione di un nuovo evento si pone il problema dell’aggiornamento della ListView
dedicata alla presentazione della lista dei pazienti ricevuta dal server : l’utente infatti mediante i
controlli disponibili sul lato sinistro della form
potrebbe aver aperto una delle finestre di
dialogo, con conseguente passaggio del flusso di
esecuzione e stallo dell’intera applicazione in
caso di richiesta di gestione di un evento. Per
ovviare a questo problema si impiega il metodo
Invoke offerto dalla maggior parte dei controlli
del namespace “System.Windows.Forms, il
quale consente l’esecuzione di un delegato che
punta ad un metodo del controllo stesso, in un
thread separato da quello principale. Sono
omessi ulteriori dettagli di carattere strettamente
implementativo.
Il metodo “Print_client_report” dell’interfaccia
permette di inoltrare al server la richiesta di
stampa del report di un singolo paziente o di
tutti quelli presenti nel database. La peculiarità di quest’operazione è l’attributo “OneWay()”: il
processo di stampa è 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 delega l’impegno ad un thread creato ex-novo.
In merito all’interazione tra il server ed il proprio monitor (descritta successivamente in dettaglio),
l’interfaccia prevede il metodo Are_you_alive: la sua dichiarazione è necessaria unicamente per
18
consentirne l’invocazione, ma l’implementazione è del tutto opzionale, come dimostra il seguente
snippet di codice:
Il motivo verrà esposto nei prossimi capitoli.
c – Cliente e servitore: attivazione.
Il modello di base per l’attivazione dei componenti prevede:

la registrazione del server presso l’infrastruttura come WellKnownObject, (sezione service
del file di configurazione) in termini di tipo, Uniform Resource Identifier (URI), canale (e
quindi porta e protocollo), modalità (Singleton o Single Call) più eventuali modifiche alla
catena dei sink (layer interessati dal passaggio delle richieste che realizzano di fatto il
pattern Interceptor) tra cui spicca il formatter, responsabile della ricostruzione del
messaggio di chiamata, a partire dai dati (grezzi o strutturati) scambiati a livello di trasporto.
I canali di default sono l’Http e il Tcp: il primo garantisce la massima interoperabilità, ma
soffre in termini prestazionali utilizzando il protocollo Soap. Il secondo è più performante
(usa stream binari per il trasporto), ma potrebbe esser bloccato da firewall, gateway etc. La
flessibilità del supporto permette comunque la definizione di canali personalizzati.
L’Updater_sink_provider verrà descritto nelle sezioni successive.
19

la dichiarazione da parte del client dei WellKnownObjects remoti cui intende accedere
(sezione client) del file di configurazione in termini di tipo e Uniform Resource Locator
(URL). La sezione channels attiva un canale di comunicazione anche per il cliente: il valore
nullo pone l’infrastruttura in attesa di una connessione su una qualsiasi delle porte libere.
I file Xml presentati poco sopra rappresentano un ottimo strumento per la configurazione rapida di
un’applicazione remota. Il programmatore deve scrivere un’unica riga di codice per rendere attiva
l’infrastruttura:
Purtroppo il meccanismo non è sempre applicabile, come nel caso del client: il file precedente è
puramente riportato a scopo dimostrativo, in quando il/gli indirizzi dei servitori sono prelevati
runtime dal server di Discovery. L’alternativa è l’attivazione dinamica dei servizi, la quale richiede
la scrittura di qualche riga di codice in più.
d – Cliente e servitore: configurazione
Entrambi i componenti richiedono l’intervento dell’amministratore in quanto, essendo responsabile
del deployment dell’applicazione è in grado di infondere conoscenza nel sistema, ovvero aggiornare
in modo opportuno i file di configurazione. Il client infatti necessita dell’Url del DiscoveryServer,
per il recupero della lista dei servizi disponibili. Il server invece richiede l’Url del manager della
copia di standby per l’aggiornamento di quest’ultima (v. oltre). Il programmatore ritiene più
consono un intervento minimo dell’amministratore, rispetto all’introduzione di un round-trip tra un
server ed il proprio Monitor.
20
e – Server e Monitor: pubblicazione e check della liveness del servizio.
Il Monitor, unico per ogni server, possiede tre compiti:



pubblicare il servizio attraverso le funzionalità del DiscoveryServer;
contattare periodicamente il server controllato, per verificarne la vitalità;
in caso di guasto, aggiornare l’entry del DiscoveryServer ed attivare la copia di standby,
interagendo con il suo controllore.
Per il proprio funzionamento,
il Monitor richiede la
presenza
nel
file
di
configurazione
App.config
degli Url relativi al servitore,
al DiscoveryServer, alla copia
di standby e al suo manager.
Tralasciando la fase di
pubblicazione che consiste
nell’invocazione del metodo
Suscribe dell’apposito server,
si ritiene utile analizzare il
meccanismo di fault tolerance: alla sua attivazione, il Monitor crea un nuovo thread responsabile
dell’inoltro periodico di messaggi di heartbeating. La richiesta consiste nell’invocazione del
metodo Are_you_alive già presentato nell’analisi dell’interfaccia del servitore. Per evitare di
introdurre carico computazionale aggiuntivo connesso a funzionalità che non riguardano la logica
applicativa, è stato sviluppato un sink lato server 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 è
posizionato immediatamente al di sopra del formatter in quanto lavora su messaggi e non stream di
dati o envelope Soap.
In caso di guasto, il monitor aggiorna l’Url del servizio con quello relativo alla copia di standby la
cui attivazione è dovuta ad un messaggio di election indirizzato al suo gestore (StandbyManager).
Tutto questo garantisce la prosecuzione del lavoro dell’utente, a parte un transitorio conseguente
alla segnalazione della caduta del server primario, alla comunicazione con il DiscoveryServer (al
fine di aggiornare l’Url del servitore) e all’ottenimento del nuovo riferimento remoto (della copia di
standby appena eletta).
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. La politica implementata nel prototipo
prevede un unico tentativo di connessione al “nuovo” Url ; sviluppi futuri saranno indirizzati al suo
raffinamento, ad esempio attraverso l’attivazione di un thread per il polling della connessione.
21
f – DiscoveryServer: pubblicazione dei servizi
Come anticipato precedentemente, il DiscoveryServer è stato introdotto sia per garantire maggior
scalabilità all’architettura, sia per supportare l’aggiornamento dinamico degli Url dei servizi (e
quindi consentire il
recovery dei client).
Il componente mantiene
al suo interno una lista
(implementata tramite
un’HashTable) formata
dall’insieme delle coppie
[NomeServizio,Url]. Il
suo
aggiornamento
runtime è di competenza
dei vari Monitor, senza
però alcun salvataggio
persistente
delle
informazioni. Per questo, l’attivazione del DiscoveryServer avviene necessariamente in modalità
Singleton. La sequenzializzazione delle richieste è ovviata dal limitato numero di client
dell’ambiente di deployment (il committente ne ha indicati al massimo otto) e dalla loro limitata
interazione con questo server (unicamente al primo accesso o in caso di failure).
Sviluppi futuri potrebbero prevedere l’attivazione del server in modalità SingleCall ed un thread
separato, demandato alla gestione dell’HashTable: in tal caso sarebbero necessarie politiche di
sincronizzazione sia per l’accesso che per l’aggiornamento dei dati. A tal fine, la classe
System.Collections.Hashtable mette a disposizione del programmatore il metodo statico
Hashtable.Synchronized(Hashtable _hashtable).
Segue l’interfaccia del servizio, dichiarata nell’assembly “General”, condiviso con i componenti che
necessitano dell’accesso al DiscoveryServer, ovvero i client ed i Monitor.
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.
L’ArrayList restituita infatti contiene tante istanze della classe serializzabile Service_def (che
banalmente memorizza la coppia di informazioni [Nome,Url] ) quanti i servizi disponibili.
22
g – StandbyManager e relativa copia del servizio: garantire fault-tolerance all’architettura.
Il punto focale per conseguire un livello basilare di tolleranza ai guasti è rappresentato dal
StandbyManager: come si può dedurre dal nome, si tratta del gestore della copia di standby del
servizio e provvede non solo
alla sua attivazione in caso
di guasto, ma è responsabile
dell’aggiornamento
del
database su cui essa opera, a
causa delle peculiarità del
prototipo.
Nei capitoli precedenti è
stata infatti descritta la
soluzione di compromesso
riguardante la gestione della persistenza dei dati: il progettista ha deciso di riservare l’adozione di
un database server a sviluppi futuri dell’applicazione (per motivi di tempo). A questo consegue
l’introduzione di una copia replicata del database, locale al StandbyManager, continuamente
aggiornata dallo stesso, in risposta alle sollecitazioni
inviate dal server primario attraverso l’interfaccia a lato.
Sono necessarie alcune osservazioni: innanzitutto, come
si può notare, gli argomenti di input dei metodi non
possiedono uno strong type, ma sono semplici array di
istanze della classe base System.Object. Questa scelta
deriva dal metodo con cui le operazioni sono invocate dal
server primario, descritto successivamente. In secondo
luogo l’attributo [OneWay()] potrebbe far nascere
qualche perplessità riguardo l’aggiornamento di tipo
Fire&Forget della copia secondaria. L’autore ha
consapevolmente adottato una politica ottimista, per i
seguenti motivi:
 la fase di checkpointing è subordinata al successo dell’esecuzione dell’operazione sul server
primario; l’occorrenza di un problema viene propagata all’utente (mediante la classe
Response), 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;
 la dimensione contenuta dell’ambiente di deployment (una rete locale configurata ad hoc)
limita fortemente la probabilità di perdita di messaggi (la quale comporterebbe un
disallineamento dei due database);
 il prototipo, inteso come tale, mira all’esposizione dell’intera architettura progettata: dettagli
di questo tipo potrebbero esser gestiti
o segnalando eventuali problemi al server primario, abbandonando la semantica
Fire&Forget delle operazioni, ma introducendo overhead o ritardi (a causa della
politica eager dell’aggiornamento) ed ulteriori meccanismi di fault-tolerance (e.g.
per un nuovo inoltro della richiesta), contro il principio di minima intrusione;
o avvisando l’amministratore del sistema, vincolando però il buon funzionamento di
quest’ultimo al tempestivo intervento umano;
23
o mantenendo la politica Fire&Forget, ma bloccando o terminando l’esecuzione del
StandbyServer a fronte di problemi, annullando in tal modo il grado di recoverability
del sistema;
Come si può notare, ognuna delle soluzioni possiede sia pregi che difetti. Qualsiasi sforzo da
parte dello sviluppatore per garantire consistenza delle copie a fronte di problemi sarebbe
inoltre vanificato dalla successiva introduzione di un database server. Per questo si
preferisce l’adozione di una politica ottimista.
Per quanto riguarda l’invocazione dei suddetti metodi, si propone un meccanismo simile a quello di
heartbeating: il server primario non è interessato a livello applicativo dal checkpointing, ma
quest’ultimo è gestito dagli strati
software
sottostanti,
attraverso
l’introduzione di un apposito sink.
L’Updater_sink analizza infatti sia i
messaggi in ingresso, sia quelli in uscita,
testando
l’avvenuta
esecuzione
dell’operazione attraverso l’enumerativo
ServerProcessing.Complete. Solo in
questo caso inoltra il messaggio di
richiesta al metodo statico a lato che si
occupa dell’estrazione dei parametri e
dell’interazione con il StandbyManager
per l’aggiornamento della copia del
database.
L’integrazione
dell’
Updater_sink
nell’infrastruttura .Net Remoting si deve
all’introduzione
della
classe
Updater_sink_provider, che implementa
l’interfaccia IServerChannelSinkProvider e provvede sia alla creazione di un’istanza di
Updater_sink sia alla sua registrazione nella catena dei sink del server primario.
L’ ultimo aspetto non ancora analizzato riguarda il meccanismo di attivazione del StandbyManager
e della relativa copia, in risposta alla ricezione di un messaggio di election: a tal fine è stata
sviluppata una classe Configurator dotata di una coppia di metodi:
 Init_copy_manager: provvede dinamicamente alla creazione / registrazione del canale di
comunicazione e di un’istanza del manager. Si noti la definizione di un event handler.
24

Disable_server: è l’handler dell’evento Disable_server generato dalla ricezione di un
messaggio di election da parte del StandbyManager: in risposta al fallimento del server
primario si procede alla deregistrazione del controllore della copia di standby e
all’attivazione (naturalmente dinamica) di quest’ultima. Tutte le informazioni riguardanti
porta ed Uri del servizio sono recuperate dal file di configurazione dell’applicazione,
permettendo una loro personalizzazione.
Il file App.config sopra riportato possiede un flag booleano “Is_primary_copy” modificabile
dall’amministratore di sistema: come si può intuire questo permette, in fase di deployment
dell’applicazione, il setting del medesimo componente come server primario o copia di standby,
incrementando la flessibilità e manutenibilità dell’architettura (anche grazie alla riduzione delle
entità in gioco).
25
Capitolo 5: conclusioni e sviluppi futuri
L’architettura discussa in questa relazione ha consentito all’autore di confrontarsi con le
problematiche caratteristiche dei sistemi distribuiti, svincolandosi dal modello Desktop delle
applicazioni precedentemente sviluppate.
Il prototipo presentato, benché non abbia alcuna pretesa di compiutezza, ha portato ad un buon
livello di comprensione della tecnologia Microsoft .Net Remoting e di sperimentazione concreta dei
concetti appresi.
Si ritiene che entrambi gli aspetti siano fondamentali per rispondere alle esigenze dei committenti di
prodotti software: molto spesso infatti non hanno loro stessi chiare le caratteristiche del servizio
desiderato e la scarsa formazione informatica concorre alla credenza comune che il passaggio da
un’applicazione monoutente ad un sistema distribuito sia gestito automaticamente dal sistema
operativo (rendendo valida la trasposizione del termine “utente” in “utonto”).
La fase di sviluppo ha richiesto il confronto dell’autore con il cosiddetto “chatty vs. chunky tradeoff”: la maggior parte delle tecniche di programmazione object oriented proposte dai testi consultati
forniscono eleganti soluzioni per problemi comuni, ma sono perlopiù appropriate nel caso in cui gli
oggetti siano “vicini” tra loro (ovvero appartengano allo stesso processo o “application domain” in
.Net). La realizzazione di applicazioni distribuite scalabili ed ottimizzate invece impone stretti limiti
sulla collaborazione delle entità pena l’introduzione di livelli inaccettabili di overhead di
comunicazione: nel caso particolare è stata necessaria una riorganizzazione del software
preesistente per ridurre al massimo l’uso di properties e callbacks, in favore di metodi con un
elevato numero di parametri (e.g. IClient_manager.Modify_client()).
Il test dell’intero lavoro è stato effettuato utilizzando il programma VMware Workstation attivando
più virtual machine contemporaneamente.
Per quanto riguarda i possibili sviluppi futuri, oltre a quelli specificati in fase di analisi dei singoli
componenti, sono state identificate le seguenti evoluzioni, più o meno necessarie (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;
26
Bibliografia

“Microsoft .Net Remoting”, S.McLean, J.Naftel, K.Williams Microsoft Press 2003;

“Advanced .Net Remoting (C# edition)”, I.Rammer, APress 2002;

“Pattern-Oriented Software Architecture – A system of patterns (volume 1)”,
F.Buschmann, R.Meunier, H.Rohnert, P.Sommerland, M.Stal, Wiley 2001;

“Pattern-Oriented Software Architecture – Patterns for Concurrent and Networked
objects (volume 2)” F.Buschmann, R.Meunier, H.Rohnert, P.Sommerland, M.Stal, Wiley 2001;

“A programmer’s guide to ADO.Net in C#”, M.Chand, APress 2002;

“Visual C# 2005 – How to program”, P.Deitel, J.Listfield, T.Nieto, C.Yaeger, M.Zlatkina,
Prentice Hall 2005;

“Distributed systems, principles and paradigms”, A.Tanenbaum, M.Van Steen, Prentice Hall
2002;

“MSDN Library – Febbraio 2005”, Microsoft, 2005;
27