La premessa a questo articolo è che tratteremo argomenti un tantino complessi che presuppongono un po’ di background, almeno teorico, per poter comprendere alcuni termini e concetti.

Qualche tempo fa ho fatto un po’ di esperimenti con Glassfish3 cercando di configurare un cluster di application server che sfruttasse al meglio anche le code JMS. Parliamo quindi di un MOM (Message Oriented Middleware).
Dalla documentazione Oracle (http://docs.oracle.com/cd/E26576_01/doc.312/e24934/jms.htm) vengono descritti 2 differenti scenari a seconda che si voglia garantire solo la disponibilità del servizio o anche preservare la disponibilità dei dati.
In questo secondo caso si parla di “enhanced broker cluster” (da adesso in poi, EAC). Il manuale apposito (http://docs.oracle.com/cd/E26576_01/doc.312/e24949/broker-clusters.htm#ggsxf) descrive ancora più in dettaglio le sue caratteristiche.
Riporto qui per semplicità solo la figura in modo da evidenziare le caratteristiche salienti:
In sostanza configuro un broker locale all’interno di ciascun nodo del mio cluster in modo che ogni istanza del cluster avvii il suo broker.
Tutti i broker del cluster condividono i dati di configurazione e lo stato in cui si trovano attraverso un database master. Anche i dati inseriti all’interno delle nostre code sono persistenti all’interno del database quindi in caso di fault da parte di uno dei nostri broker è sempre possibile per un altro di essi riprendere il lavoro da dove si era interrotto.
Ovviamente a questo punto della vicenda, il nostro tallone d’Achille diventa il database ma in figura si parla di “Highly available data store” quindi in base alle funzionalità offerte dal vostro DB vendor preferito occorrerà mettere in piedi un sistema di failover del database o una configurazione in replica o qualunque altra cosa vi permetta di risolvere il problema.
Se il tema generale è chiaro allora possiamo passare alla parte operativa di creazione e configurazione.
Best practice: creazione e configurazione di un utente ad hoc
Questo primo punto è una banalità ma lo voglio sottolineare perchè molto spesso non viene rispettato e magari le applicazioni vengono lanciate da root (non dite di no!) o da un utente più o meno casuale.
Il vantaggio di creare l’utente dedicato, in questo caso “glassfish”, mi permette di rintracciare i processi più facilmente, di lavorare in un ambiente dedicato con tutte e sole le configurazioni di cui ho bisogno.
Nel mio caso specifico, all’interno della home vado a creare il file .profile con le seguenti variabili di sistema:
export GLASSFISH_HOME=/home/glassfish/glassfish3
export MQ_HOME=/home/glassfish/glassfish3/mq
export JAVA_HOME=/home/glassfish/java
PATH=”$JAVA_HOME/bin:$GLASSFISH_HOME/bin:$MQ_HOME/bin:$PATH”
I path che vedete sono dei link simbolici che puntano alle reali cartelle di installazione. In alternativa potete anche mettere tutto nella home, dipende da che tipo di ordine volete ottenere sul vostro server 😉
Passo1: Creazione del dominio
Il mio approccio alla configurazione del cluster è stato quello di usare tanto i tool a riga di comando quanto la console di amministrazione di Glassfish.
Per la creazione del dominio ho usato il comando “asadmin”:
lcianci@lcianci-dell:/pto/AppServer/glassfish3/bin$ ./asadmin
Use “exit” to exit and “help” for online help.
asadmin>
Una volta entrati in asadmin, passo a creare un nuovo dominio specificandone il nome
asadmin> create-domain hlite
Enter admin user name [Enter to accept default “admin” / no password]> hlite
Enter the admin password [Enter to accept default of no password]>
Enter the admin password again>
Using default port 4848 for Admin.
Using default port 8080 for HTTP Instance.
Using default port 7676 for JMS.
Using default port 3700 for IIOP.
Using default port 8181 for HTTP_SSL.
Using default port 3820 for IIOP_SSL.
Using default port 3920 for IIOP_MUTUALAUTH.
Using default port 8686 for JMX_ADMIN.
Using default port 6666 for OSGI_SHELL.
Using default port 9009 for JAVA_DEBUGGER.
The file in given locale [it_IT] at: [/pto/AppServer/glassfish3/glassfish/lib/templates/locales/it_IT/index.html] could not be found. Using default (en_US) index.html instead.
Distinguished Name of the self-signed X.509 Server Certificate is:
[CN=lcianci-dell,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
Distinguished Name of the self-signed X.509 Server Certificate is:
[CN=lcianci-dell-instance,OU=GlassFish,O=Oracle Corporation,L=Santa Clara,ST=California,C=US]
No domain initializers found, bypassing customization step
Domain hlite created.
Domain hlite admin port is 4848.
Domain hlite admin user is “hlite”.
Command create-domain executed successfully.
Best practice: Creazione di un file per l’autenticazione (opzionale)
Dato che per agire con “asadmin” all’interno del mio nuovo dominio dovrò sempre connettermi specificando utente e password, mi semplifico un po’ la vita creando il file hlite_passwd che contiene all’interno due variabili:
AS_ADMIN_USER=hlite
AS_ADMIN_PASSWORD=hlite
Queste ovviamente sono le credenziali che ho inserito al passo1 in fase di creazione del dominio.
In questo modo evito di inserire le credenziali ogni volta e posso collegarmi in questo modo:
asadmin -W hlite_passwd
Passo2: Avvio del dominio
Passo semplice e indolore:
asadmin> start-domain hlite
Waiting for hlite to start …..
Successfully started the domain : hlite
domain Location: /pto/AppServer/glassfish3/glassfish/domains/hlite
Log File: /pto/AppServer/glassfish3/glassfish/domains/hlite/logs/server.log
Admin Port: 4848
Command start-domain executed successfully.
Se siamo curiosi e vogliamo esplorare la nostra console la troviamo su localhost:4848.
Passo3: Creazione dei nodi applicativi del cluster
Il cluster che ho in mente per i miei test è composto da due nodi: la mia macchina e un server remoto. Il nodo master, dal quale accedo eventualmente alla console è la mia macchina.
Una volta creato il dominio, andiamo quindi a configurare il primo nodo, quello di default, che è appunto localhost
asadmin> list-nodes-config
localhost-hlite CONFIG localhost
Command list-nodes-config executed successfully.
Creiamo invece un nodo ssh per usare la nostra macchina di sviluppo remota. Per questo secondo passo di creazione del nodo uso direttamente la console di amministrazione dato che a questo punto è ormai up&running.
Passo4: Creazione del database per i broker JMS
Creo il mio database master sulla macchina remota di sviluppo utilizzando mysql:
mysql> create database glassfish_mq;
Query OK, 1 row affected (0.00 sec)
Ricordatevi di creare l’utente e assegnare le grant:
mysql> grant all on glassfish_mq.* to glassfish@’%’ identified by ‘glassfish’;
Query OK, 0 rows affected (0.00 sec)
Passo5: Caricamento delle librerie per il database
Affinchè la rete di broker JMS possa accedere al nostro database, occorre copiare le librerie mysql (il JAR) in questa cartella:
GLASSFISH_HOME/mq/lib/ext/
Passo6: Configurazione del cluster jms enhanced
Occorre configurare e definire i broker di tipo LOCAL e non EMBEDDED per entrambi i due nodi, quello locale e quello remoto.
Per fare questo uso la console. (proprietà JMS del file di configurazione del cluster)
Una volta completata l’operazione, possiamo tornare alla riga di comando di asadmin:
asadmin> configure-jms-cluster –passwordfile hlite_passwd –clustertype=enhanced –dbvendor mysql –dbuser glassfish –dburl jdbc:mysql://IP_SERVER_DB:3306/glassfish_mq clop-hlite
 
JMS Cluster Configuration updated for Cluster clop-hlite
Command configure-jms-cluster executed successfully.
Tutto chiaro?
Il mio dominio si chiama HLITE mentre il cluster applicativo si chiama CLOP-HLITE.
Adesso passiamo alle istanze dei nodi.
Passo7: Creazione e avvio delle istanze sul cluster
Uso la console per creare 4 istanze, 2 locali e 2 remote che chiamo:
  • local-cl01, local-cl02
  • remote-cl01, remote-cl02
A questo punto, sempre utilizzando la console di amministrazione, fate partire il cluster avviando soltanto la prima istanza locale.
Se tutto va bene dovrebbe partire correttamente e guardando i log vi accorgerete che sono state create le tabelle sul database di configurazione.
Scoprirete anche che il broker name di questa prima istanza è il seguente: clophlitelocalcl01@lcianci-dell e risponde alla porta 27676.
Ripetete l’operazione per le altre istanze.
Avremmo potuto far partire direttamente l’intero cluster ma così abbiamo controllato la corretta creazione del database e l’avvio di un singolo broker.
Passo8: Definizione delle risorse JMS
Adesso, dalla console di amministrazione , andiamo a configurare le risorse JMS usando l’apposito TAB.
Creiamo la nostra QueueConnectionFactory (QCF) e gli oggetti Queue che andremo ad usare ricordando che occorre impostare per ciascuno il target cluster.
Il nome della QCF sarà quello che poi andremo ad usare all’interno delle applicazioni di test.
Passo9: Deploy delle applicazione di test (EAR, EJB e WAR)
Per testare il tutto può bastare una semplice applicazione enterprise (modulo EAR) con dentro una servlet (modulo WAR) che accoda i messaggi quando la chiamate e un MessageDrivenBean (modulo EJB) che scoda i messaggi restando in ascolto sulla coda.
Per il deploy del modulo EAR usiamo naturalmente la console.
Passo10: Test e verifiche
Una volta che le applicazioni di test sono installate sulle 4 istanze, vi basterà creare un semplice script di shell o una suite Jmeter per andare a chiamare ripetutamente le vostre servlet e simulare quindi le chiamate applicative.
Per verificare che a livello dei broker tutto funzioni correttamente, possiamo usare un altro utile tool a riga di comando “imqcmd” ricordando che occorre impostare preventivamente utente e password jms mediante la console alla voce “provider jms”. In particolare ecco 3 utili comandi:
imqcmd list bkr -u admin -b lcianci-dell:27676
 
imqcmd query bkr -u admin -b lcianci-dell:27676
 
imqcmd query dst -t q -n sampleQueue -u admin -b lcianci-dell:27676
Gli attributi sono abbastanza espliciti ma ad ogni modo:
-u: utente
-b: broker (nome macchine + porta)
-t: tipo della destination [Queue (q) oppure Topic (t)]
-n: nome della destination (la mia coda di test)
Ecco quindi l’output del mio cluster enhanced mentre viene bombardato dal mio script sh di test:
imqcmd list bkr -u admin -b lcianci-dell:27676
Listing all the brokers in the cluster that the following broker is a member of:
 
————————————————————————————————————–
ID of broker Time since last
Broker ID Address State Msgs in store performing takeover status timestamp
————————————————————————————————————–
clophlitelocalcl02 192.168.0.209:27677 OPERATING 278 19 seconds
clophliteremotecl01 192.168.0.19:27676 OPERATING 419 20 seconds
clophlitelocalcl01 192.168.0.209:27676 OPERATING 340 21 seconds
clophliteremotecl02 192.168.0.19:27677 OPERATING 475 13 seconds
 
Successfully listed brokers in the cluster.
Tips: Deploy e upgrade delle applicazioni con continuità di servizio
Dato il cluster applicativo, diventa molto semplice, anche mentre eroghiamo il servizio, andare ad aggiornare le applicazioni sui singoli nodi. Questa ovviamente non è una best practice ma è sempre meglio sapere come fare. L’ideale è comunque prevedere un minimo di disservizio e fare le cose con calma.
Per poter effettuare il deploy di una nuova versione del mio EAR mi basta crearne una copia con un nome leggermente diverso, poi con la context root continuerò ovviamente a chiamare i componenti applicativi sempre con le stesse url.
Supponiamo quindi di aver fatto il deploy iniziale del file TestCode_EAR_1.0.ear
Per aggiornare la versione, ricordiamo che il target in questo caso è il mio cluster e quindi tramite asadmin:
asadmin deploy –enabled=false –target clop-hlite /home/lcianci/export/TestCode_EAR_1.1.ear
In questo modo ho caricato la nuova versione dell’ear ma questa non è ancora attiva.
Disattivo adesso il vecchio ear su una singola istanza. Qui il target è la mia istanza:
asadmin disable –target local-cl01 TestCode_EAR
Infine attivo il nuovo ear sulla singola istanza
asadmin enable –target local-cl01 TestCode_EAR_1.1
Nel frattempo il mio script di test continua a bombardare il cluster e adesso, 3 applicazioni su 4 sono vecchie mentre la quarta è aggiornata.
Conclusioni
Come anticipato, il tema non era semplice e nelle parti relative alla console ho dato per scontato che chi legge si sappia muovere un minimo tra i meandri di Glassfish. Immagino che se non sia così forse non eravate nemmeno interessati al tema.
In generale noto che con la configurazione di base, il cluster non mi sembra eccessivamente performante però dobbiamo tenere conto che sto usando due istanze in locale sulla mia macchina.
Sono inoltre fiducioso che seguendo i consigli di fine-tuning della documentazione si riesca comunque a migliorare le performance.
Il cluster jms enhanced è comunque per definizione meno performante della sua versione base sia essa master-slave o di tipo peer.
Ovviamente non tutte le circostanze presuppongono l’uso di questo tipo di MOM quindi occorre sempre valutare per bene i pro e i contro di una determinata scelta architetturale.