JBoss 7 e Infinispan, una cache per tutti i gusti

Con la nuova versione dell’Application Server firmato JBoss, ovvero JBoss AS 7, tra le mille modifiche e le mille cose cambiate, troviamo anche JBoss Cache. La nuova gestione della cache, a differenza della precedente, è confluita sotto l’ala di Infinispan, ovvero una piattaforma di caching distribuita e scalabile, pensata appositamente per data grid. Per noi comuni mortali che significa? Significa che possiamo contare su un prodotto open source che ben presto diventerà una implementazione, almeno in parte grazie al modulo infinispan-cdi, della nuova specifica JCache (JSR-107) che verrà probabilmente inclusa nella Java EE 7. Vale quindi la pena dargli un’occhiata da vicino.

Tipi di cache

Infinispan permette di avvalersi di due tipi di cache:

local cache
cache disponibile in un’unica istanza dell’Application Server.
replicated cache
cache distribuita tra i nodi di un cluster, condivisa specificando un opportuno transport che si avvale di JGroups.

La loro configurazione è piuttosto semplice, seguendo il modello dei subsystems introdotto da JBoss 7. Aprendo infatti uno dei file di configurazione:

$JBOSS_HOME/domain/configuration/domain.xml
Se lavoriamo in Domain Mode
$JBOSS_HOME/standalone/configuration/standalone-ha.xml
Se lavoriamo in Standalone Mode. Se abbiamo intenzione di lavorare con una cache locale, basta configurare standalone.xml

e cerchiamo il subsystem con urn urn:jboss:domain:infinispan:1.2 (quella che al momento è presente in JBoss 7.1.1): quello che troviamo di default è la cache di Hibernate che possiamo prendere come esempio per capire come configurare una nuova cache. E’ possibile comunque validare la sintassi di questo subsystem a fronte dello schema presente in

  • $JBOSS_HOME/docs/schema/jboss-infinispan.xsd, oppure in
  • $JBOSS_HOME/docs/schema/jboss-as-infinispan_1_2.xsd

a seconda dalla versione di JBoss 7 che avete a disposizione.

Un tuning accurato della cache richiederebbe un post a parte, per adesso limitiamoci a vedere come riuscire a mettere in piedi una cache in pochi minuti!

Mettiamo le mani in pasta

Per prima cosa dobbiamo aver chiaro che tipo di cache abbiamo bisogno: una che scade dopo un certo periodo di tempo? Una che salva un certo numero di oggetti e via via invalida quelli meno usati? Una cosa combinata? Prendiamo per esempio la seguente definizione:

<cache-container name="expiration-cache" default-cache="daily-cache" start="EAGER">
   <local-cache name="daily-cache">
      <eviction strategy="LRU" max-entries="1000"/>
      <expiration lifespan="86400000"/>
   </local-cache>
</cache-container>

Abbiamo definito un nuovo cache-container, ovvero un contenitore di cache locali e/o distribuite. Al container viene dato un nome, raggiungibile tramite JNDI, il nome della cache di default e un hint (start="EAGER") che dice al container di avviare la cache contestualmente all’avvio dell’Application Server. Quest’ultimo attributo si rende necessario se, al momento del lookup del CacheContainer, viene sollevata un’eccezione che ci informa che il container non può essere iniettato nel nostro bean perché non è avviato. Come lo inietta dite? Semplicemente così:

...
@Resource(lookup = "java:jboss/infinispan/container/expiration-cache")
private org.infinispan.manager.CacheContainer cacheContainer;
...

Questo JNDI è vero per JBoss 7.1.1, mentre per le versioni precedenti di JBoss 7 (giusto per semplificarci la vita) è java:jboss/infinispan/expiration-cache!

Una volta ottenuto il cache container, prendiamoci la nostra local cache! Come si presenta? Sarà una mappa generica che estende ConcurrentMap di Java, le cui proprietà sono state definite nell’xml precedente: ovvero scadrà dopo un giorno (86400000 millisecondi) e conterrà al massimo 1000 elementi. Una volta configurata, per poter usare una cache di questo tipo bastano poche righe di codice (come insegna il wiki di Infinispan + JBoss 7)


@ManagedBean
public class MyBean<K, V> {
  @Resource(lookup="java:jboss/infinispan/container/expiration-cache")
  private org.infinispan.manager.CacheContainer container;
  private org.infinispan.Cache<K, V> cache;
 
  @PostConstruct
  public void start() {
    this.cache = this.container.getCache();
  }
}

Avendo specificato default-cache="daily-cache" sul container, siamo sicuri di ottenere la cache desiderata. Se avessimo definito più cache, basta passarne il nome al metodo getCache() del container.

Qualche informazione in più

A livello di cache, i tag eviction ed expiration sono quelli più usati. Quali attributi è possibile configurare?

eviction
definisce le modalità per cui un elemento viene espulso dalla cache.
  • strategy (default NONE)
    definisce l’algoritmo di espulsione
    • UNORDERED: vengono cancellati in modo disordinato i valori per rimanere nei limiti delle dimensioni della cache. Ha il vantaggio di non soffrire dei costi di ordinamento dei metodi successivi.
    • FIFO (First In First Out): vengono esclusi gli elementi più vecchi, ovvero quelli inseriti per primi.
    • LRU (Least Recently Used): vengono esclusi quelli usati meno frequentemente.
    • LIRS (Low Inter-reference Recency Set): stessa strategia di LRU ma più performante
    • NONE: disabilita l’espulsione
  • max-entries (default 10000)
    definisce il massimo numeri di elementi che può contenere la cache. -1 indica nessun limite. Il valore deve essere un intero che sia potenza del 2. Se così non fosse, viene preso il minimo valore superiore a quello indicato che sia potenza del 2.
expiration
determina per quanto tempo deve essere valida una cache. -1 indica tempo infinito.
  • max-idle (default -1)
    determina per quanto tempo di inattività un elemento viene mantenuto nella cache, in millisecondi
  • lifespan (default -1)
    definisce il tempo di vita di un elemento nella cache, in millisecondi.
  • interval (default 5000)
    indica in millisecondi, ogni quanto tempo viene eseguito l’algoritmo di controllo sugli elementi della cache.

Conclusioni

Con una annotazione e poco xml abbiamo accesso alla cache fornita dall’Application Server, che possiamo iniettare in tutti i nostri bean. Ricordate il post in cui parlavamo degli interceptor di CDI? Avevamo improvvisato una cache rudimentale con un EJB Singleton: sostituendolo con la gestione della cache come abbiamo visto in questa occasione riusciamo ad anticipare il funzionamento di JCache che vedremo prossimamente!

Andrea Como

Sono un software engineer focalizzato nella progettazione e sviluppo di applicazioni web in Java. Presso OmniaGroup ricopro il ruolo di Tech Leader sulle tecnologie legate alla piattaforma Java EE 5 (come WebSphere 7.0, EJB3, JPA 1 (EclipseLink), JSF 1.2 (Mojarra) e RichFaces 3) e Java EE 6 con JBoss AS 7, in particolare di CDI, JAX-RS, nonché di EJB 3.1, JPA2, JSF2 e RichFaces 4. Al momento mi occupo di ECM, in particolar modo sulla customizzazione di Alfresco 4 e sulla sua installazione con tecnologie da devops come Vagrant e Chef. In passato ho lavorato con la piattaforma alternativa alla enterprise per lo sviluppo web: Java SE 6, Tomcat 6, Hibernate 3 e Spring 2.5. Nei ritagli di tempo sviluppo siti web in PHP e ASP. Per maggiori informazioni consulta il mio curriculum pubblico. Follow me on Twitter - LinkedIn profile - Google+