Recientemente, desde Viafirma hemos revisado la arquitectura técnica de un proyecto Java EE que estaba teniendo problemas de escalabilidad. En Desarrollo todo iba correctamente, pero con un número importante de registros (apenas 100.000) había tiempos de respuesta que se podían ir a los 5 minutos. Son tiempos no admisibles, por lo que urgía detectar fuentes de problemas y optimizar los procesos.
Problema de estabilidad y solución con Ehcache
El problema no era un código defectuoso, sino una implementación extremadamente flexible que permitía al usuario realizar búsquedas complejas que implicaban grandes volúmenes de consultas y operaciones sobre listas. Desde filtrados a operaciones de unión/intersección de listas enormes, pasando por persistencia, etc. Muchas de estas listas, como las de datos geográficos (por ejemplo, provincias), no cambiaban frecuentemente, por lo que resultaba óptimo implementar una caché de objetos.
Decidimos utilizar Ehcache debido a su facilidad de integración con Hibernate, que ya hacía uso de este. Además, Ehcache ofrece múltiples ventajas funcionales, como la gestión automática de la caducidad de los objetos cacheados y la capacidad de manejar grandes volúmenes de datos serializados en disco, lo que lo hace ideal para mejorar el rendimiento de una aplicación Java EE.
Ventajas de usar Ehcache
Gestión de caducidad: Ehcache elimina automáticamente los objetos cacheados después de un tiempo predefinido, devolviendo
NULL
cuando la caché expira.Control del número de objetos: Permite configurar la cantidad de objetos almacenados en memoria. Si se supera el límite, Ehcache serializa los objetos adicionales a disco, gestionando eficientemente los recursos.
Compatibilidad con Clustering: Ehcache soporta configuraciones distribuidas, lo que permite el uso en entornos de cluster.
Eliminación de caché en el shutdown: Ehcache elimina la caché de manera controlada durante el apagado del servidor de aplicaciones.
¡No reinventar la rueda!
Implementación de una Caché con Ehcache
Desarrollamos una sencilla clase de tipo Manager, siguiendo el patrón Singleton, que interactúa con Ehcache para almacenar y recuperar objetos en caché que anteriormente se generaban online. El resultado fue una optimización impresionante: operaciones que antes tardaban 5 minutos pasaron a ejecutarse en menos de 10 segundos, logrando una mejora de más del 3000% en rendimiento.
Aquí te dejo un fragmento del código de la clase CacheUtil
para implementar esta caché:
/*
* File: CacheUtil.java
*
* Created on march 2009
*
*
* Copyright 2006-2029 Javier Echeverría Usúa (javieu at gmail.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.viavansi.framework.core.cache
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.viavansi.framework.core.persistencia.servicios.excepciones.ExcepcionServicio;
/**
* @author Javier Echeverria Usua (javieu at gmail.com)
* @author Felix Garcia Borrego (borrego at gmail.com)
* @author Alexis Castilla Armero (pencerval at gmail.com)
*/
public class CacheUtil {
public Object getCachedObject(String key) throws Exception{
if(minutosVidaDefault == 0)
minutosVidaDefault = MINUTOS_DEFAULT;
return getCachedObject(key, minutosVidaDefault);
}
public void putCachedObject(String key, Object info) throws Exception{
if(minutosVidaDefault == 0)
minutosVidaDefault = MINUTOS_DEFAULT;
putCachedObject(key, info, minutosVidaDefault);
}
public Object getCachedObject(String key, int minutosCache) throws Exception{
Cache cacheCustodia = getInstance(minutosCache);
Element element = cacheCustodia.get(key);
if(element != null){
return cacheCustodia.get(key).getValue();
}
else{
return null;
}
}
public void putCachedObject(String key, Object info, int minutosCache) throws Exception{
Cache cacheCustodia = getInstance(minutosCache);
cacheCustodia.put(new Element(key,info));
}
private Cache getInstance(int minutosCache) {
Cache cache = null;
//Todavía no existe
int timeTolife = minutosCache * 60;
CacheManager manager = CacheManager.getInstance();
String nombreCache = "viavansiCachedInfo" + minutosCache;
if (manager.cacheExists(nombreCache)) {
cache = manager.getCache(nombreCache);
} else {
// Configuramos el apagado en caso de Error de la JVM o parada inesperada.
System.setProperty("net.sf.ehcache.enableShutdownHook", "true");
manager.addCache(new Cache(nombreCache, 100, true, false, timeTolife, timeTolife));
cache = manager.getCache(nombreCache);
}
return cache;
}
public static void shutDown() {
CacheManager.getInstance().shutdown();
}
private int MINUTOS_DEFAULT = 60;
private static int minutosVidaDefault = 0;
protected CacheUtil(int minutosVida) {
minutosVidaDefault = minutosVida;
}
public static void init(int minutosVida) {
minutosVidaDefault = minutosVida;
}
/*
* Constructor y accesores
*/
// Patrón Singleton
private static CacheUtil singleton;
//Instancia de Commons Logging
private Log log = LogFactory.getFactory().getInstance(this.getClass().getName());
/**
* private constructor (solo una instancia).
* @return AmbitoBO
*/
protected CacheUtil() throws ExcepcionServicio {
super();
log.debug(«Creando nueva instancia de CacheUtil»);
}
/**
* Devuelve instancia activa de AmbitoBO.
* Típico método de implementación de patrón singleton
* @return AmbitoBO
* @throws ExcepcionServicio
*/
public static CacheUtil getCurrentInstance() throws ExcepcionServicio {
if (singleton == null) {
try {
singleton = new CacheUtil();
} catch (ExcepcionServicio e) {
throw e;
}
}
return singleton;
}
}
Un saludo a todos, y poned una caché en vuestros corazones :-P