Sviluppare Applicazioni (Java) Web Cluster Aware – Parte I

Molti Application Server (da IBM WebSphere a JBoss AS) offrono la possibilità di essere installati nel contesto di un’architettura in Cluster. In un Cluster l’Application Server gira su più nodi (che possono coinvolgere macchine fisiche o virtuali diverse), allo scopo di ottenere Failover e Balancing.

Esplicitiamo cosa significano questi concetti:

  • Failover: l’applicazione continuerà a essere disponibile anche se il nodo su cui si è acceduta durante la precedente richiesta è “caduto”
  • Balancing: è possibile aumentare il carico di lavoro che l’Application Server può supportare distribuendo di volta in volta le richieste sui nodi meno “affaticati”

Nomenclatura e Concetti base relativi agli Application Server Clusterizzati

Chi si trova a lavorare in una architettura clusterizzata all’inizio può essere spiazzato nell’affrontare la relativa nomenclatura e alcuni concetti cardine. Li illustro brevemente di seguito, facendo riferimento all’Application Server IBM WebSphere.

  • Cella: è un’unità virtuale costituita da un Deployment Manager e uno o più nodi
  • Deployment Manager: è un processo (in realtà è una vera e propria istanza speciale di WebSphere) responsabile della gestione, installazione e manutenzione delle applicazioni, dei Connection Pool e di altre risorse relative all’ecosistema J2EE. Infine è responsabile della centralizzazione del repository degli utenti e del servizio di autenticazione e autorizzazione di WebSphere. Il Deployment Manager comunica con i nodi tramite un altro processo speciale di WebSphere, il Node Agent
  • Nodo: unità virtuale costituita da un Node Agent e una o più istanze di server
  • Node Agent: processo responsabile che crea ed eventualmente uccide i processi server e della sincronizzazione della configurazione tra il Deployment Manager e il nodo
  • Server: processo Java responsabile della gestione di richieste J2EE (serve ad esempio pagine JSP/JSF, chiamate EJB, scodando le code JMS etc)
  • Cluster: unità virtuali che raggruppano i Server in modo che le risorse aggiunte ad un Cluster siano propagate ad ogni Server che lo compone: di solito infatti questo si compone di un numero di nodi maggiore di uno

La nostra applicazione in Cluster

Le richieste HTTP che l’Application Server riceve dall’utente di una applicazione Web vengono quindi dirottate su un nodo disponibile. Successive richieste dello stesso utente, per questioni di performance, sono dirottate possibilmente sullo stesso nodo (session affinity), ma non vi sono garanzie a riguardo: la non disponibilità del nodo o una strategia di bilanciamento del carico potrebbero fare sì che successive richieste dello stesso utente vengano di volta in volta dirottate. Si dice che l’applicazione deployata su un Application Server in Cluster è una applicazione distribuita.

Salva lo stato!

Una prima conseguenza del fatto che la nostra applicazione gira in una architettura in Cluster è che il suo stato deve essere replicato su tutti i nodi del Cluster. Sappiamo che, nonostante il protocollo HTTP sia stateless (non è possibile capire che una richiesta viene dallo stesso utente che l’ha fatta in precedenza), le applicazioni Web per loro natura possono non esserlo: si pensi a una applicazione di e-commerce e al concetto di Carrello della spesa che essa implica.

Lo sviluppatore implementa il salvataggio dello stato (utente loggato, carrello della spesa, parametri utilizzati nell’ultima ricerca e così via) memorizzando uno o più oggetti in sessione. Cosa succede quanto la nostra richiesta viene dirottata da un nodo all’altro? Lo stato deve essere allo stesso modo essere trasferimento nel nuovo nodo di lavoro. Poiché questo può implicare un cambio della Java Virtual Machine (JVM) si deve ricorrere al meccanismo della serializzazione.

Serializzare un oggetto significa trasformarlo in una sequenza di byte allo scopo di ricostruirlo in un momento successivo. Chi si occupa di tenere sincronizzata la sessione tra i nodi? Per fortuna l’Application Server :-)!

Bene, io non devo fare nulla quindi…

Si potrebbe pensare che lo sviluppatore possa prescindere dal sapere dove verrà installata la propria applicazione e che il fatto che essa sia distribuita o meno sia assolutamente trasparente per lui. In realtà anche se l’Application Server si occupa di fare tutto il lavoro ha bisogno che lo sviluppatore segua alcune regole: in caso contrario non riuscirà a replicare lo stato e l’applicazione Web non funzionerà correttamente.

Doh! Quindi cosa devo fare?

Abbiamo detto che l’Application Server serializza gli oggetti che si trovano in sessione per ricostruirli poi su un altro nodo (e forse in un’altra JVM). La prima regola è quindi che tutti gli oggetti in sessione siano serializzabili. Quindi implica il fatto che implementino l’interfaccia serializable:

import java.io.Serializable;
public class OneSerializableClass implements Serializable
{
	private int count;
        ...    ...
}

L’interfaccia serializable è alquanto bizzarra: non possiede alcun metodo da implementare, è soltanto un “marker” che informa che gli oggetti del tipo che la implementano sono serializzaibli. Il suo scopo è di informare subito l’algoritmo di serializzazione se deve o meno procedere con la serializzazione dell’oggetto (in molti casi non ha senso o non si vuole serializzare un oggetto, si pensi ad un oggetto Thread!).

Tuttavia non è così semplice: affinché un oggetto dichiarato serializable lo sia davvero anche tutto il suo stato (i suoi field) devono essere serializable e via così in modo ricorsivo.

import java.io.Serializable;
public class OneSerializableClass implements Serializable
{
	private int count;

        // must implement java.io.Serializable
        private OneField oneField;
}

E se non faccio queste cose?

In realtà la nostra applicazione sembra funzionare correttamente finché non si verifica un cambio di nodo. In realtà ad esempio su IBM WebSphere ci sono policy che tengono sincronizzata la sessione ogni 10 secondi (valore di default modificabile da console) per cui se la sessione non è serializzabile si notano nei log errori di tipo NotSerializableException. Ad aggravare la situazione ci può essere la circostanza per cui in ambiente di sviluppo l’Application Server non è clusterizzato, ma in ambiente di Certificazione/Produzione sì: il passaggio in produzione in questo caso diventa di quelli da Apocalypse Now!

Continua…

Nella seconda parte di questo post andremo a illustrare altre accortezze che bisogna avere quando si utilizza una implementazione JPA (ad esempio EclipseLink) per gestire la persistenza e un framework JSF per creare la GUI della nostra applicazione Web deployata su un Application Server Clusterizzato.

Manuele Piastra

Sono uno Scrum Master e Project Manager i cui skill tecnici sono focalizzati al momento sullo sviluppo di applicazioni Java EE su IBM Websphere 7.0 utilizzando JSF (RichFaces), JPA (EclipseLink) ed EJB3. Presso OmniaGroup ricopro il ruolo di Training Manager: seleziono il personale tecnico, mi occupo della sua crescita formativa organizzando Corsi e Workshop sia interni che esterni, molti dei quali hanno visto me come docente. LinkedIn Profile - Google+