lunes, 24 de agosto de 2009

Almacenamiento en caché de objetos Java: JCS y la inicialización de las regiones de cache

En este post voy a explicar cómo inicializar las regiones de JCS en el contexto de una aplicación web. Cualquier otro contexto como una aplicación de escritorio debería ser un caso más simple con el cual trabajar. (Para más detalles o documentación oficial es preferible visitar directamente el sition de Jakarta's)

Una vez que se han incluido todos los jars necesarion para correr JCS, necesitamos defininir cómo vamos a cargar las regiones del cache. Una region en JCS es un grupo de configuraciones que son aplicadas a cualquier objeto que guardado bajo esa región de cache. Por ejemplo en una región está definido el tiempo de expiración de un objeto, la cantidad máxima de objetos que se permite guardar, uso de un auxiliar de cache para reducir la carga de memoria, etc.
Podemos definir tantas regiones como requiramos, sin embargo es necesario pensar en un buen diseño de las regiones para establecer una buena categorarización de manera que en el futuro se nos facilite la monotirización de nuestro cache.

Por ejemplo, si definimos solamente una región para nuestra implementación de cache y decidimos guardar 5 clases distintas de objetos bajo una región, un tiempo después cuando estemos monitoreando la “proporción de éxito de cache” (“cache hit rate”), se nos resultará dificultoso determinar cuáles clases están contribuyendo en buen o mal “hit rate”.

En caso de que alguién no esté familiarizado con el concepto de CACHE HIT. Se refiere al evento en el cual hacemos una consulta al cache por un objeto en especifico y resulta que satisfactoriamente el objeto estaba guardado en cache. El caso contrario se le conoce como un “CACHE MISS” o “desacierto de cache”. Es siempre deseable para un buen rendimiento de la aplicación mantener una buena proporción de cache hits. El uso de cache implica un nuevo consumo de recursos como RAM (almacenamiento de objetos) y tiempo de CPU (cálculos para encontrar los objetos en los arreglos del cache) así que si solo vamos a obtener unos cuantos hits, podríamos estar gastando nuestros recursos innecesariamente.

Mi opinion personal, dada la experiencia que he tenifo usando JCS en la aplicación web con la que trabajo, es que es mejor definir las regiones acorde con las clases de objetos usados porque en nuestro caso guardamos objetos de cache provenientes de distintos servicios web. Es mejor saber cuáles clases “cachiables” están contribuyendo al mejoramiento del rendimiento total de la aplicación.

Un colega una vez sugerió que deberíamos definif las regiones acorde con los periodos de vida, por ejemplo:

* ShortLifeRegion
* MediumLifeRegion
* LongLifeRegion
* EternalLifeRegion

Este es otro tipo de categorización y realmente no hay una receta para determinar cuál es la mejor estratefia. Es una decisión técnica que tiene que ser tomada en cada caso particular.

El caso más comun para definir las regiones es trabajar con un archivo de propiedades y cargarlas una única vez.


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.InvalidPropertiesFormatException;
import java.util.List;
import java.util.Properties;
import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
import org.apache.jcs.engine.behavior.IElementAttributes;
import org.apache.jcs.engine.control.CompositeCacheManager;
import org.apache.jcs.engine.control.event.behavior.IElementEventHandler;

public class CacheManager {

public static void init(String cacheConfigPath) {

InputStream in = null;
CacheInitializerThread initializerThread =
new CacheInitializerThread();
try {
in = new FileInputStream(cacheConfigPath);
Properties prop = new Properties();
prop.load(in);
CompositeCacheManager ccm = CompositeCacheManager
.getUnconfiguredInstance();
ccm.configure(prop);

} catch (FileNotFoundException e) {
logger.logError(e);
} catch (InvalidPropertiesFormatException e) {
logger.logError(e);
} catch (IOException e) {
logger.logError(e);
}
finally {
try {
if (in != null) {
in.close();
in = null;
}
}
catch (Exception e) {}
}
}
...
}

Así que en nuestra aplicación podríamos llamar es métofo init en una clase Plugin.


public class CachePlugin extends Plugin {

private void init(ActionServlet servlet) {
String cacheConfigPath=
servlet.getServletContext().getRealPath(“cache.cff”);
CacheManager.init(cacheConfigPath);

}

}

Las siguientes propiedades definen una región llamada “medium”:

# medium live time refresh objects
jcs.region.medium=
jcs.region.medium.cacheattributes=org.apache.jcs.engine.CompositeCacheAttributes
jcs.region.medium.cacheattributes.MaxObjects=10000
jcs.region.medium.cacheattributes.MemoryCacheName=org.apache.jcs.engine.memory.lru.LRUMemoryCache
jcs.region.medium.cacheattributes.UseMemoryShrinker=true
jcs.region.medium.cacheattributes.ShrinkerIntervalSeconds=300
jcs.region.medium.cacheattributes.MaxMemoryIdleTimeSeconds=18000
jcs.region.medium.cacheattributes.MaxSpoolPerRun=100
jcs.region.medium.elementattributes=org.apache.jcs.engine.ElementAttributes
jcs.region.medium.elementattributes.IsEternal=false
jcs.region.medium.elementattributes.MaxLifeSeconds=18000


Cualquier objeto guardado en esta región expirará en 5 horas (MaxLifeSeconds=18000) y permitirá guardar solamente un máximo de 10000 objetos.

No hay comentarios:

Publicar un comentario