Jboss In Action Capitolo 12 - Understanding clustering

JavaEE non specifica il modo in cui bisogna fare il cluster. Ogni application server lo implementa in maniera differente. I nodi di JBoss si riconoscono uno con l'altro automaticamente sulla rete, permettendo di abilitare facilmente una tolleranza ai guasti con pochissimo codice.

12.1 Understanding clustering

Le macchine di un cluster sono chiamate nodi, prima di poter parlare di cluster dobbiamo parlare di load balancing.

12.1.1 Load balancing

In figura si vede sulla sinistra un sistema senza load balancing tutte le richieste dei client (gli schermi) su un singolo server, sulla destra invece si vede il sistema bilanciato.
load-balancing.jpg
Le richieste arrivano a un singolo punto di ingresso del sistema e vengono distribuite ai server. Il balancing non è una caratteristica dell'application server ne dell'applicazione.
Il cluster non è usato per abilitare la caratteristica del load balancing ma fornisce le informazioni al load balancer per semplificare l'applicazione.
NOTA: il load balancing è una maniera per fare scalare applicazioni che usano codice sincrono, per le richieste asincrone non si usa il load balancing. Spesso nelle asincrone si vuole che una singola istanza dell'applicazione manipoli tutte le richieste.

TYPES OF LOAD BALANCERS

Ci sono due tipi di load balancing hardware e software. Hardware sono più costosi ma anche più affidabili.
I load balancer hanno un indirizzo ip visibile per i client e poi mappano una rete di ip interni (possono essere virtuali) per ogni nodo del cluster. Quando riceve una richiesta il load balance riscriver l'header e lo indirizza a una macchina.
Il load balancer realizza hight avalability riconoscendo le macchina che vanno giù e non inoltrando a loro richieste.
Quando sulle macchine è presente lo stato il load balancer deve realizzare il server affinity per indirizzare una sotto sequenza di azioni sempre allo stesso nodo.
Il load balancer hardware che realizzano hight avalability, server affinity e fast performance sono difficili da configurare e costosi.
Ci sono diversi load balancer di tipo software sia della microsoft che lato unix, ma per applicazioni JavaEE solitamente si usano gli stessi web server per adempiere a questo compito. JBoss Web Server , Apache o ISS attenzione al consumo di risorse dei load balancer software.
Il load balancer funziona un po' come una DMZ mediando le richieste e impedendo l'accesso diretto alle macchine.
Ci sono diverse strategie di load balancing disponibili.

  1. Random: si commenta da sola
  2. Round robind: manda le richieste a una lista di server in maniera sequenziale
  3. Stichy session (o first avaliable): la prima richiesta viene mandata usando una random o una round robin e poi una volta stabilita una sessione dirige una sottosequenza di richieste allo stesso server.

Vi sono anche altri modi usando sistema dei pesi e politiche di distribuzione.

ROUND-ROBIN DNS
È un sistema scongliato per diversi motivi. Funziona fornendo ai client il server da una lista di ip al momento della risoluzione del nome. Questo comporta diversi problemi:

  1. Client e dns server spesso tengono in cache il risultato della richiesta di dns, questo può portare a sbilanciamento del carico.
  2. Non si adempie al server affinity anche se il server va giu
  3. Non si ha il pieno controllo perchè viene ceduto praticamente al client

12.1.2 Cluster topology and makeup

La topologia è il modo in cui i nodi del cluster vengono disposti. Quando i nodi del cluster sono su macchine differenti allora sono detti orizzontali, quando si trovano sulla stessa macchina sono versticali. La formazione di un cluster di solito prevede l'utilizzo di entrambe. Un nodo di solito è una istanza di JBoss. I cluster orizzontali sono più facili da istallare rispetto ai verticali, ma questi ultimi evitano la latenza di rete nelle comunicazioni.
Qual'è più performante tra i due modelli? Se una macchina ha molta RAM e più processori e all'avvio dell'applicazione rimangono risorse inutilizzate conviene mettere più di un nodo sulla stessa. Il cluster verticale ovviamente è meno resistenze ai guasti. Il verticale è più facile da aggiornare.
JBoss permette di usare facilmente cluster verticali, orizzontali e misti grazie all'automatic discovery, i nodi di JBoss si riconoscono automaticamente senza bisogno di configurare niente.
I nodi possono avere deployato al loro interno diverse applicazioni o servizi. Se due nodi hanno lo stesso ambiente si dicono omogenei altrimenti eterogenei.
Alcuni servizi come il high-availability JNDI lavorano meglio se i nodi sono omogenei ma in alcuni casi di risorse limitate i cluster eterogenei sono inevitabili.

12.1.3 Automatic discovery and multicasting

Abbiamo già detto che JBoss permette il discover automatico degli altri nodi del cluster. Il nodo che va su per primo diventa il coodinatore, questo significa che gli altri nodi devo iscriversi ad esso per essere parte del cluster. La caratteristica che permette il discover automatico e quindi la cominicazione di gruppo è detta multicast. Come si vede in figura ogni nodo ha un indirizzo proprio e utilizza per un indirizzo e uno di multicast a questo è associata anche una porta. indirizzi-dei-nodi.jpg
La soluzione migliore è quando è disponibile a livello di rete il protocollo multicast con UDP, se la rete non lo permette lo si simulerà con un unicast, questo impatta sulle performance e genera molto traffico di rete.
JBoss usa un tool chiamato JGroups per la comunicazione tra nodi , questi è un toolkit per la comunicazione affidabile in multicast. JGroups abilita diverse caratteristiche usate nel cluster di JBoss includendo il supporto per diffferenti protocolli di trasporto, automatic discover, reliability, failure detection e cluster membership-management services. JGroups permette la configurazione multicast su udp o multicast simulato (più unicast su UDP).

12.1.4 High availability

Ci sono due modi per raggiungere High availability. Se l'applicazione è stateless basta il load balancing. Se è stateful i server devono avere un meccanismo per il failover che trasferisce lo stato dal server che va giù ad un altro, questo deve essere fatto dal load balancer. Ovviamente è più facile raggiungere un alta affidabilità con nodi orizzontali. Nel libro vi è anche una tabella che lascia un po' il tempo che trova su percentuale di uptime e il downtime per anno.

12.1.5 Replication and fault tolerance

Nel caso di applicazioni stateful lo stato deve essere conservato.
Due tipi di dati sono tipicamente associati con lo stato dell'utente. Dati di sessione e di entità. I dati di sessione sono mantenuti in memoria dall'applicazione o dal sistema di cache alcuni esempi sono HTTP sessions and SFSBs. I dati di entità sono mantenuti in una copia principale nel db e poi vi sono le copie nelle applicazioni EJB3 entitities sono degli esempi.
Per ottenere la fault tollerance è necessario che una copia dei session data deve essere disponibile fuori dal nodo che ne è il proprietario. JBoss usa una clustered cache che può replicare le sessioni in cache attraverso i nodi di un cluster questa si chiama state replication. Quando un server fallisce il client si sposta su un altro tutto perchè la sessione viene continuamente replicata su un altro server.
Per i dati di entità basta che il database sia sempre disponibile.

TYPES OF STATE REPLICATION

C'è un compromesso verso il quale si deve andare . Per garantire la fault-tollerance si deve avere una replicazione sincrona dello stato, ma questo può essere molto lento in caso di cluster con molti nodi.
L'alternativa è la replicazione asincrona questa però non garantisce il 100% di recupero. Ma perdere la sessione alla fine è un evento raro che non crea eccessivi problemi anche perchè lo stato critico solitamente è tenuto nel db e non in memoria.

TOTAL REPLICATION VERSUS BUDDY REPLICATION

Si chiama total state replication quando ogni nodo di un cluster replica il proprio stato su ogni altro nodo del cluster. Questo porta a un consumo di memoria e di cpu per effettuare gli aggiornamenti. Per alleviare questi problemi JBoss fornisce due metodi buddy replication e state passivation (di quest'ultimo si discuterà nella sezione 12.1.6).
Buddy replication permette di replicare solo attraverso un subset di nodi che appartengono al tuo buddy , ovviamente anche il recupero può avvenire solo attraverso nodi che sono buddy con te.
Come si vede in figura buddy-replication.jpg
dal lato sinistro si vede un nodo che replica il proprio stato con un altro buddy. Nella parte destra si vede come il cluster si adatta al momento del fallimento. Il buddy come si vede dall'esempio mantiene i propri dati più quelli di un altro nodo il suo buddy. La replicazione buddy è disponibile su ogni cosa che gira sulla JBoss Cache, così può essere usato per replicare la sessione http e SFSB .

INVALIDATING CACHED ENTITY DATA

Molti nodi possono avere dati di entità (che ovviamente si possono ) replicati nella loro cache, se si vuole invalidare la cache tutti i nodi devono ricevere un messaggio che invalidi la loro cache e obblighi a rileggere dal db. Invalidazione è più veloce della replicazione perchè genera meno traffico di rete.

12.1.6 State passivation

Alcune applicazioni per loro natura devono tenere aperta la sessione per un lungo periodo di tempo, questo consuma memoria anche se la sessione è inattiva per un lungo periodo di tempo. Session passivation può essere usata per risolvere questo problema. Le sessioni possono essere attivate nuovamente quando serve o eliminate se scade il timeout.
Se la cache è già piena e un nuovo utente ne richiede un altra, una delle vecchie sessioni viene salvata nel db. Se il vecchio utente ha bisogno di nuovo della sessione questa viene recuperata dal db, bisogna ovviamente stare attenti al trade off.

12.1.7 Distribution versus clustering

troppo base per riassumerla, non ne vale la pena. In due parole distribuzione dei livelli dell'applicazione o replicazione dell'applicazione in toto.

12.2 Setting up a simple cluster

12.2.1 Bringing up a JBoss cluster

Nella configurazione all di JBoss vi è tutto quello che serve per avviare in modalità cluster, quindi bisogna avviarlo con l'opzione -c all. Facciamo una prima prova per attivare JBoss sulla stessa macchina, abbiamo bisogno di due indirizzi se siamo su linux vanno bene localhost e l'indirizzo locale 192.168.10.194 per esempio. Se siamo su windows si dovranno fare alcuni passi che si trovano sul libro che non riporto. Dopo di che andare sulla cartella server e copiare la configurazione all in node1 e node2

cp all node1
cp all node2

Aprire due console e lanciare per la prima il comando
./run.sh –c node1 –b localhost –Djboss.messaging.ServerPeerID=1

-c come detto specifica la configurazione, -b specifica l'indirizzo , –Djboss.messaging.ServerPeerID setta un ID unico per il message service dei nodi di JBoss che è richiesto perchè il messagin del cluster funzioni appropriatamente.
A console si avrà qualcosa del genere.
[DefaultPartition] Initializing partition DefaultPartition
[DefaultPartition] Number of cluster members: 1
[DefaultPartition] Other members: 0
[DefaultPartition-HAPartitionCache] JBoss Cache version: JBossCache 'Alegrias' 2.1.1.GA
[DefaultPartition] Fetching serviceState (will wait for 30000 milliseconds): [DefaultPartition] State could not be retrieved (we are the first member in group)

[DefaultPartition] è un altra parola per dire cluster.
Attiviamo anche il secondo node del cluster
./run.sh –c node2 –b 192.168.10.194 –Djboss.messaging.ServerPeerID=2

Attenzione a specificare bene i parametri che cambiano cioè la configurazione e l'id
Come si può vedere all'avvio del nuovo nodo il primo da un messaggio che indica la sottoscrizione
16:16:53,548 INFO  [DefaultPartition] New Members : 1 ([192.168.10.194:1099])
16:16:53,548 INFO  [DefaultPartition] All Members : 2 ([127.0.0.1:1099, 192.168.10.194:1099])

Come si può vedere non è stato necessario dire a nessuno dei due che esisteva l'altro l'automatic discovery funziona. Per controllare che siano up basta andare su http://localhost:8080 e http://192.168.10.194:8080

Possibili errori che ho riscontrato

DEPLOYMENTS IN ERROR:
  Deployment "jboss.messaging:service=PostOffice" is in error due to the following reason(s): java.lang.IllegalArgumentException: Cannot start post office since there is already a post office in the cluster with the same node id (0). Are you sure you have given each node a unique node id during installation?, **ERROR**

Che si vede subito all'avvio
run.sh: unused non-option argument: –Djboss.messaging.ServerPeerID=2
16:57:16,648 INFO  [ServerImpl] Starting JBoss (Microcontainer)...
16:57:16,655 INFO  [ServerImpl] Release ID: JBoss [Morpheus] 5.0.1.GA (build: SVNTag=JBoss_5_0_1_GA date=200902232048)

12.2.2 Creating a clustered EJB

Creiamo un applicazione distribuita sui cluster. Bisogna un server che viene sviluppato su entrambi nodi del cluster un .jar e un client che si lancerà tramite un java project normale. La strategia di balancing di default è la round-robin.
Il server sarà un EJB che stampa a video i numeri che riceve, il client invece manda i numeri al server.
Serve per il client un interfaccia e la sua implementazione ecco il codice. Per funzionare servono le librerie contenute in jbossall-client che praticamente sono quasi tutte quelle di client/ nella dir di jboss. Per maggiori dettagli vedere jbossall-cllient.jar/readme.txt . Le librerie non è necessario che stiano nel jar perchè essendo deployato su jboss vede tutte le lib dell'application server. La libreria dov'è contenuta l'annotazione @Clustered è jboss/client/jboss-ebj3-ext-api.jar

package test;
import javax.ejb.Remote;
@Remote
public interface Counter
{
    public void printCounter(int messageNumber);
}
package test;
import javax.ejb.Stateless;
import org.jboss.ejb3.annotation.Clustered;
@Stateless
@Clustered
public class CounterBean implements Counter
{
    @Override
    public void printCounter(int messageNumber)
    {
     System.out.println(messageNumber);
    }
}

Come si vede serve una annotazione al bean che indica al server che questo bean deve essere load balanced (di questo si discuterà maggiormente nel cap 13)

12.2.3 Deploying your application

Il jar creato deve essere messo nelle cartelle di deploy. Un modo per non dover replicare il tutto in caso di server omogenei è usare una sola cartella condivisa con un protocollo di rete.

12.2.4 Calling the clustered EJB

Ecco il client che comunica con il jboss

package test;
import javax.naming.Context;
import javax.naming.InitialContext;
public class Client {
  public static void main(String[] args) throws Exception {
    InitialContext ctx = new InitialContext();
    Counter s = (Counter) ctx.lookup("CounterBean/remote");
    for (int i = 0; i < 100; i++) {
      s.printCounter(i);
      Thread.sleep(1000);
    }
  }
}

Deve esserci nella dir root dir del client (io l'ho messo nella cartella src di Eclipse) il file jndi.properties con queste proprietà settate
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming

Mandando in running il client dopo aver deployato correttamente i server si vede che le richieste vengono smaltite in maniera bilanciata da uno dei due nodi del cluster.

12.3 Understanding JBoss clustering

Tutti i servizi di clustering sono costruiti su JGroups come si vede in figura
jgroups-architetture.jpg
non tutti i servizi mostrati sono spiegati nel libro.

12.3.1 Understanding the JGroups architecture

Due dei componenti principali sono il canale e il protocol stack.
canale-protocollo-jgroups.jpg
Un canale fornisce un modo all'applicazione per connettersi e mandare messaggi a altri membri del cluster. Come si vede in figura quando un messaggio viene mandato percorre i livelli dello stack.
Ogni livello nello stack consiste in un protocollo. In JGroups un protocollo non corrisponde necessariamente a un protocollo di trasporto. Un protocollo in JGroups può mandare, ricevere, modificare, riordinare, passare o droppare un messaggio. Per esempio un FRAG protocol può frammentare un messaggio in uscita in varie parti e riassemblandolo quando viene ricevuto. La tabella 12.3 fa un riassunto sui protocolli in JGroups.
Servizi differenti possono usare canali differenti, configurare canali multipli necessita più threads e quindi CPU context switching. È più efficente usare un canale JGroups singolo che multiplexa il traffico di cluster , questo è quello che fa JBoss fuori dalla scatola. Tutti servizi di cluster di JBoss sono configurati per girare nel top di un canale singolo multiplexato come si vede nella figura sovrastante.
protocols-jgroups.jpg

12.3.2 Configuring JBoss clustering services

Ogni servizio del cluster in JBoss ha i suoi file di configurazione nella directory server/all/deploy/cluster/cluster-jboss-beans.xml nella tabella vi è un riassunto devi vari servizi.
Ogni servizio di cluster devo puntare al canale e al protocollo di stack che vuole usare. La configurazione è simile in ogni file prendiamo cluster-jboss-beans.xml , questo file di configurazione del microcontainer definisce un bean chiamato HAPartition.

<bean name="HAPartition"
    class="org.jboss.ha.framework.server.ClusterPartition">
 ...
 <property name="cacheManager">
   <inject bean="CacheManager"/>
 </property>
 <property name="cacheConfigName">
   ha-partition
 </property>
 <property name="partitionName">
   ${jboss.partition.name:DefaultPartition}
 </property>

La proprietà partitionName definisce il nome per il cluster, altre configurazioni possono chiamare questa stessa proprietà clusterName invece di partitionName.
La proprietà cacheManager punta a un altro bean che amministra la configurazione della cache di JBoss. La cache manager punto a un singolo canale di JGroups che JBoss definisce e ogni configurazione di cache deve puntare a un protocollo di stack.
La proprietà cacheConfigName definisce il nome della configurazione di cache da usare. The cache manager bean is defined in server/all/deploy/cluster/jboss-cache-manager.sar/META-INF/jboss-cache-manager.beans.xml.
The cache configurations che il bean usa è definita in jboss-cache-configs.xml nella stessa directory.
<bean name="CacheManager" >
...
 <property name="channelFactory">
   <inject bean="JChannelFactory"/>
 </property>
Salvo diversa indicazione, il contenuto di questa pagina è sotto licenza Creative Commons Attribution-ShareAlike 3.0 License