MokaByte 83 - Marzo 2004 
Apache & Tomcat
Come connettere i due server per realizzare architetture scalabili, sicure performanti
II parte: Coyote e JK2
di
Giovanni Puliti
Dopo aver introdotto da un punto di vista teorico il concetto di connettore Apache-Tomcat, ed aver visto alcuni di connettori più usati, questo mese proseguiamo con quello che rappresenta lo stato dell'arte attuale e probabilmente diventerà il connettore più importante del futuro: il Coyote JK e JK2

La architettura Coyote
Coyote non è propriamente un connettore come gli altri, è piuttosto un'architettura realizzata in Java per la realizzazione di connettori lato Tomcat. In particolare sono disponibili due implementazioni: Coyote-HTTP e Coyote-JK, che sostituiranno, rispettivamente, la parte Tomcat dei connettori per il protocollo HTTP e la controparte di mod_jk (che precisamente sarà sostituita dalla sua evoluzione mod_jk2). Coyote è distribuito insieme a Tomcat 4.1, ma è già disponibile sia per Tomcat 3.

 

Il connettore JK
JK2 rappresenta una completa reingegnerizzazione del precedente JK rispetto al quale è certamente più potente.
Anche se è in grado di lavorare con Apache 1.3, JK2 è stato sviluppato pensando a Apache 2.0 e si adatta meglio a server HTTP multithreaded come IIS, NES/iPlanet. Può essere anche utilizzato in modalità embedded all'interno di altre applicazioni Java.
In JK2 è data maggior importanza alla separazione fra lo strato del protocollo ed quello di comunicazione fisica.
Una delle conseguenze più importanti è che JK2 può supportare i fast unix-socket, e può essere facilmente esteso per utilizzare altri protocolli di comunicazione: in questa release offre un supporto migliore e più integrato per JNI ed è stato annunciato per le prossime release l'integrazione con la New I/O API del JDK 1.4 (vedi [NIO]).
Infine vi è la possibilità di integrare il connettore con un sistema di monitoring simile a JMX: già fin da adesso è possibile agganciare JK2 con mod_status di Apache 2.0 mentre è possibile l'integrazione con altri adapters per il controllo dello stato e della configurazione di runtime.
La modalità di configurazione è stata modificata per seguire il modello a componenti: in aggiunta ai file è possibile utilizzare le cosiddette multiple configuration sources le quali forniscono un migliore sistema di integrazione in architetture emebedded.
Un'altra interessante caratteristica è la possibilità di lavorare in modalità JNI, all'interno della quale JK2 può essere infatti usato come library JNI per accedere a risorse di sistema di vario tipo fra cui la shared memory, unix domain sockets, signals, chuid, Windows registry.
In tutti questi casi sempre utilizzando lo stesso sistema di comunicazione e nelle modalità in process ed out of process.

 

Installazione di JK2
La prima cosa da fare è reperire il connettore mod_jk2 che per la piattaforma Windows si trova sotto forma di una DLL denominata mod_jk2.dll (la controparte unix si chiama mod_jk2.so).

NOTA: in [CARO] e [GRI] si fa riferimento al fatto che è stata testata solo la versione ottenuta con compilazione dinamica.

Questo file deve essere salvato nella dir di Apache APACHE_HOME/modules.
Nel caso in cui si desideri utilizzare JK con JNI si esegua lo stesso procedimento con il file jkjni.dll (o analogamente jkjni.so).
A questo punto è necessario editare i vari file di configurazione sia di Apache (httpd.conf e workers2.properties) che di Tomcat (jk2.properties, server.xml file).

Il file httpd.conf dovrebbe essere nella dir 'APACHE_HOME/conf/: esso contiene tutte i principali parametri di configurazione del server HTTP .
La prima cosa da fare è aggiungere nella porzione dei loading dei moduli, una dichiarazione di caricamento per il modulo mod_jk nel seguente modo (d'ora in poi si farà riferimento ai file per unix)

<IfModule !mod_jk2.c>
LoadModule jk2_module modules/mod_jk2.so
</IfModule>

Questo meccanismo dell' IF inclusivo è una pratica ormai in uso da diverso tempo nei file di configurazione di Apache ed evita il di ricaricare il file due volte.

Il file jk2.properties in genere è posizionato nella dir TOMCAT_HOME/conf: questo file contiene tutte le direttive di configurazione. In questo caso si procederà a configurare i parametri per la shared memory, cosa necessaria per consentire la comunicazione Apache-Tomcat.
E' convenzione posizionare il file di sharing nella directory APACHE_HOME/logs, anche se tale file non è un file di log vero e proprio.

#Shared memory directive
shm.file=c:/Apache2/logs/jk2.shm

Questo è tutto per il momento sul file jk2.properties. Per maggiori informazioni si può fare riferimento alla documentazione ufficiale sul sito di Jakarta anche se da più parti sono stata denunciate numerose imprecisioni, lacune ed errori. Si faccia quindi molta attenzione a quanto li riportato.

Il file server.xml come ormai dovrebbe essere noto contiene tutte le informazioni di lavoro di Tomcat. Procederemo quindi ad effettuare le opportune modifiche per abilitare JK2.
Seguendo la procedura mostrata nell'articolo precedente si potrà per prima cosa attivare o disattivare il connettore HTTP semplicemente commentando o scommentando la seguente parte

<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8080" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"
acceptCount="100" debug="0" connectionTimeout="20000"
useURIValidationHack="false" disableUploadTimeout="true"/>

Invece la porzione seguente è molto importante per poter attivare il connettore Coyote per il JK2

<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8009" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"
acceptCount="10" debug="0" connectionTimeout="20000"
useURIValidationHack="false"/>

Come per il connettore JK anche in questo caso la porta utilizzata è la 8009
Il resto del file server.xml contiene molti parametri di configurazione per la cui spiegazione si puà fare riferimento a [TOM].
Infine il file workers2.properties, collocato nella APACHE_HOME/conf, è il file più importante per consentire il corretto funzionamento del JK2.
Nella sua versione più semplice questo file contiene solamente due direttive: la SHM (che deve corrispondere con quanto indicato nel file jk2.properties file) e la seconda direttiva che invece definisce il worker.
Nell'esempio riportato poco sotto il worker che si definisce è di tipo TCP socket connection chiamato channel.socket.

#define the shared memory file
[shm]
file=etc/apache/logs/jk2.shm

# Define the communication channel
[channel.socket:localhost:8009]
tomcatId=localhost:8009

[ajp13:localhost:8009]
channel=channel.socket:localhost:8009

[uri:/examples/*]
worker=ajp13:localhost:8009


La direttiva URI non è obbligatoria ma è necessaria per permettere l'utilizzo di Tomcat in associazione con determinate URL di invocazione. In questo pezzo di codice si è quindi definito un worker di tipo channel socket che lavora sulla porta 8009 per l'host locaohst (quindi si assume che Tomcat sia in esecuzione sulla macchina locale).
Lo URI /examples/* attiverà Tomcat a rispondere a tutte le richieste del tipo http://localhost/examples/xxxx ma non http://localhost/examples per la quale si dovrà attivare un URI corrispondente

[uri:/examples]
worker=ajp13:localhost:8009


Anche per questo file non è disponibile al momento moltissima documentazione. Si può fare riferimento alla documentazione ondine ufficiale la quale denota ancora qualche lacuna.
Ovviamente anche in questo caso sono state tolte le parti relative all'attivazione del connettore HTTP 1.1 non necessarie in produzione ma certamente utili in fase di sviluppo e test.

 

JK2 connector con JNI
Il connettore JK2 come precedentemente annunciate offre la possibilità di utilizzare la Java Native Interface (JNI) per mettere in comunicazione Tomcat ed Apache. In questo caso, benché concettualmente ed anche a livello di configurazione le cose siano alquanto più complesse si hanno notevoli vantaggi sia dal punto di vista delle prestazioni che delle funzionali-tà. Nel primo caso il vantaggio è ovvio: le chiamate non sono eseguite tramite chiamate via socket ma tramite chiamate native in memoria.
Per ciò che concerne il secondo aspetto invece l'utilizzo del connettore nativo offre la possi-bilità di effettuare modifiche alla configurazione di Tomcat senza la necessità di effettuare un restart di Apache. Inoltre Tomcat finisce in questo caso per essere un processo figlio di Apa-che e quindi il servlet container viene mandato in esecuzione solo nel momento del reale bi-sogno (non è chiaro ancora se è possibile anche effettuare uno stand by durante i periodi di inattività).
Al solito questi vantaggi sono significativi solamente in uno scenario di produzione non durante il sviluppo.
Installazione e configurazione di JK2-JNI
Analogamente al caso precedente il connettore JK2-JNI è contenuto nel file mod_jk2.dll o mod_jk2.so il quale al solito dovrà essere inserito nella directory dei moduli di Apache ovve-ro in APACHE_HOME/modules.

NOTA: in [CARO] e [GRI] si fa riferimento al fatto che è stata testata solo la versione ottenuta con compilazione dinamica.

La configurazione è praticamente identica al caso precedente: si dovranno al solito configurare i file httpd.conf il jk2.properties il server.xml ed il workers2.properties.
Il caricamento del mod_jk2 viene indicata in Apache al solito all'interno del file http.conf utilizzando la stessa direttiva vista in precedenza.

In jk2.properties fra le alter cose troveremo l'indicazione della direttiva sulla shared memory: definizione e collocazione (APACHE_HOME/logs) seguono la stessa struttura vista in precedenza

#Shared memory directive
shm.file=/etc/apache2/logs/jk2.shm

A questo punto si deve definire una lista di handler per la comunicazione nativa

# Set the desired handler list
handler.list=apr,request,channelJni


Infine l'handler arp viene qui dichiarato come "inprocess" in modo che Apache possa lanciarlo direttamente

# If set to inprocess the mod_jk2 will Register natives itself
# This will enable the starting of the Tomcat from mod_jk2
apr.jniModeSo=inprocess


Nel file server.xml si dovrà al solito inserire la parte che attiva il funzionamento del connettore Coyote come visto in precedenza

<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"
port="8009" minProcessors="5" maxProcessors="75"
enableLookups="true" redirectPort="8443"
acceptCount="10" debug="0" connectionTimeout="20000"
useURIValidationHack="false"/>


Il file workers2.properties contiene infine tutte le informazioni più importanti per attivare la comunicazione Apache-Tomcat.
Posizionato nella dir APACHE_HOME/conf nella sua configurazione più semplice esso necessità solamente di due direttive: l'indicazione sul file SHM (che deve corrispondere con quanto definito nel jk2.properties) e la direttiva di definizione del worker.
In questo caso la configurazione è più complessa rispetto al caso precedente dato che il connettore JK2-JNI deve poter sapere come effettuare lo startup e lo shutdown del container, ovvero del server ovvero della JVM.
In [GRI] sono stati riscontrati alcuni problemi di caricamento delle librerie necessarie all'esecuzione di Tomcat. La soluzione proposta in questo caso, un po' brutale a dire il vero, consiste nel mettere tutti i jar rilasciati con la distribuzione di Tomcat direttamente nella TOMCAT_HOME/lib.
La prima direttiva definisce lo shared memory file

[shm]
file=/etc/apache2/logs/jk2.shm
size=10485760

# Define the comunication channel
[channel.jni:jni]
info=The jni channel, used if tomcat is started inprocess

La direttiva seguente, per quanto indicata come non obbligatoria nella documentazione, si rende necessaria in alcune configurazione SO+JVM senza un motivo apparente. Probabilmente si tratta di un bug non documentato

[config:]
file=/etc/apache2/conf/workers2.properties
debug=0
debugEnv=0

Alcuni parametri di configurazione della JVM

# Define the parameters for the Java Virtual Machine
[vm:]
info=Parameters used to load a JVM in the server process
OPT=-Djava.class.path=d:/Tomcat4.1/lib
OPT=-Dtomcat.home=d:/Tomcat4.1
OPT=-Dcatalina.home=d:/Tomcat4.1
OPT=-Xmx128M

# JNI worker startup handler
[worker.jni:onStartup]
info=Command to be executed by the VM on startup. This one will start tomcat.
class=org/apache/jk/apr/TomcatStarter
ARG=start
stdout=/etc/apach2/logs/stdout.log
stderr=/etc/apache2/logs/stderr.log

# JNI worker shutdown handler
[worker.jni:onShutdown]
info=Command to be executed by the VM on shutdown. This one will stop tomcat.
class=org/apache/jk/apr/TomcatStarter
ARG=stop

Infine il mapping necessario ad attivare il container in ascolto su determinati URI mapping

# Uri mapping
[uri:/examples]
info=Example webapp in the default context.
context=/examples

[uri:/examples/*]
info=Map the whole webapp

 

 

Altri connettori
Di seguito per completezza di trattazione sono riportati alcuni cenni su altri connettori disponibili in passato per Apache-Tomcat. Non sono praticamente più utilizzati e quindi non verranno trattati approfonditamente.

Il connettore JServ
Il connettore JServ (modulo per Apache mod_jserv) è storicamente il primo connettore scritto per l'integrazione tra un webserver e Tomcat. Oggi à ancora usato in produzione da molti siti, ma non dovrebbe più essere utilizzato, dal momento che non viene più manutenuto né aggiornato: da sottolineare che la documentazione ufficiale di Tomcat 3.3 su questo punto è poco chiara, e continua a parlare di JServ come connettore di default. Il connettore utilizza i protocolli AJP nelle versioni 1.1 ed 1.2 che sono supportati anche da altri connettori più recenti. Vista la sua obsolescenza, questo connettore non verrà qui ulteriormente approfondito.

Il connettore Webapp
Il connettore Webapp (modulo mod_webapp)è stato un connettore dalla vita piuttosto breve. Nato come evoluzione del precedente JServ doveva offrire prestazioni notevolmente migliori rispetto al passato. Utilizza un protocollo alternativo all'AJP, il WARP in onore alla famosa velocità a curvatura della astronave Enterprise della serie televisiva Star Trek.
Putroppo a causa dei problemi implementativi (notevoli problemi di propagazione delle credenziali di autenticazione in HTTPS con server IIS) e la mancanza per il supporto per il clustering hanno in breve tempo relegato questo connettore ad un ruolo da comprimario, fino a renderlo praticamente intulizzato.
Questo connettore è basato sull'Apache Portable Runtime (APR), un insieme di librerie C che consentono un'elevata portabilità delle applicazioni. Il connettore ne avrebbe beneficiato essendo in generale più facilmente portabile degli altri: per esempio esiste una versione funzionante sotto OS X. La configurazione di Webapp è in generale più semplice di quella di mod_jk ed è pensata per il concetto di web application.

 

Conclusioni
Si conclude questa miniserie dedicata ai connettori per Tomcat. Fra i molti presentati alla fine la scelta dovrebbe ricadere in modo piuttosto automatico su JK o meglio su JK2: quest'ultimo in particolare a detta di molti, non offre ancora sufficienti garanzie in scenari di produzione per alcuni motivi, primo fra tutti la mancanza di documentazione affidabile ed esauriente.
E' presumibile che questo sia un aspetto che a breve verrà risolto. Per chi dovesse realizzare una struttura in tempi brevi è presumibile che una scelta più conservativa possa rappresentare una soluzione migliore.

 

Bibliografia
[GRI] Tomcat-Apache using JK2 connector
http://www.greenfieldresearch.ca/technical/jk2_config.html
[CARO] - Apache2 (DSO), Tomcat 4.1, and JK2 Linux How-To
http://caro-coops.org/bb/viewtopic.php?p=478
[NIO] - New I/O APIs
http://java.sun.com/j2se/1.4.2/docs/guide/nio/
[TOM] - "Apache Tomcat" ed. Wrox aa.vv.

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it