OSCache: Sistema de caché para aplicaciones Java
Índice de contenido
- Introducción.
- Caso de estudio: Aplicación de escritorio.
- Caso de estudio: Aplicación Web.
- Referencias a otros sistemas de caché Open Source
- Conclusiones
Introducción
Normalmente hay secciones y datos dentro de nuestras aplicaciones que no varían con frecuencia o cuya frecuencia está controlada y es conocida.
Además, obtener esta información es costosa en tiempo o recursos informáticos.. pues bien, es obvio que si cacheamos esta información el rendimiento de nuestra aplicación incrementará
y por lo tanto la satisfacción de los usuarios que la utilizan también mejorará.
En este tutorial vamos a ver uno de los sistemas de caché más extendidos y aceptados, se trata de OSCache
Caso de estudio: Aplicación de escritorio
A continuación, vamos a programar una sencilla aplicación de escritorio en donde aprenderemos a realizar las tareas de cacheo, descacheo y acceso a la información cacheada. Se trata de una sencilla aplicación en donde cada operación se realiza al hacer clic en un botón, y en donde se mostrará el tiempo que transcurre desde que se inicia la tarea hasta que finaliza. Para ello, se simula el acceso a una fuente de datos cuyo costo en tiempo es bastante elevado, por lo que decidimos cachearlo.
La intención de esta sencilla aplicación es que aprenda y observe lo que va sucediendo al acceder a datos cacheados, acceder a datos que no han sido cacheados, etc.
Configuración de OSCache para este ejemplo
OSCache se configura fácilmente a través de un archivo de configuración de nombre oscache.properties
.
Aunque para usarlo no hay por que tocar nada, en este tutorial vamos a cambiar el comportamiento de por defecto (cache en memoria sin límite en el número de elementos a cachear) para habilitar el cacheo en disco,
de manera que si la aplicación se cierra no haya que volver a cachear los datos.
Desde mi punto de vista las propiedades que he modificado son las más importantes, así que observe los comentarios para conocer su significado.
# Habilitamos el cache en disco cache.persistence.class=com.opensymphony.oscache.plugins.diskpersistence.DiskPersistenceListener # Especificamos el directorio donde se cacheará la información cache.path=c:\\oscache_temp # Indicamos que queremos un límite también para los elementos cacheados en disco cache.unlimited.disk=false # Como mucho serán cacheados 500 objetos simultánemente cache.capacity=500 # Cuando se supere el límite de 500 objetos, se aplicará el algoritmo LRU (Least Recently Used) para obtener espacio en disco y cachear los nuevos elementos. cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache
Para más información dirigase a la documentación de configuración oficial.
Código fuente de la aplicación:
La siguiente clase simula un acceso lento (2 segundos) para obtener una información que luego será cacheada.
package com.autentia.tutoriales.oscache; /** * Gestión de clientes. * Simula accesos lentos a los datos. * @author Carlos García. Autentia. * @see http://www.mobiletest.es */ public class CustomersCtrl { private CustomersCtrl(){} /** * @return Devuelve todos los clientes */ public static java.util.ArrayListgetAll(){ java.util.ArrayList customers = new java.util.ArrayList (); for (int i = 0; i < 5; i++){ customers.add("Cliente " + String.valueOf(i)); } // Retrasamos la ejecución, simulando una operación costosa en tiempo try { Thread.sleep(2000); } catch (java.lang.InterruptedException ex){ // No hacemos nada. } return customers; } }
La siguiente clase representa una ventana con distintos botones cuyas tareas estarán realicionadas con operaciones con la caché.
package com.autentia.tutoriales.oscache; import java.awt.*; import java.awt.event.ActionEvent; import java.util.ArrayList; import javax.swing.*; import com.opensymphony.oscache.general.GeneralCacheAdministrator; import com.opensymphony.oscache.web.filter.ExpiresRefreshPolicy; import com.opensymphony.oscache.base.NeedsRefreshException; /** * Ventana para probar el API de OScache * @author Carlos García. Autentia. * @see http://www.mobiletest.es */ public class OSCacheTestFrame extends JFrame implements java.awt.event.ActionListener { private static final String ALL_CUSTOMER_CACHE_KEY = "CUSTOMERS"; // Atributos funcionales private GeneralCacheAdministrator cache; private ArrayListcustomers; // Atributos gráficos private JLabel lblTime; private JPanel contentPane; private JPanel buttons; private JButton btnAppendDataToCache; private JButton btnReadFromCache; private JButton btnDeleteCache; /** * Constructor */ public OSCacheTestFrame(){ super("Pruebas con OScache"); cache = new GeneralCacheAdministrator(); this.createUI(); } /** * Crea el interfaz gráfico */ private void createUI() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception ex) { //No se dará } lblTime = new JLabel(); contentPane = new JPanel(new BorderLayout()); buttons = new JPanel(new FlowLayout()); btnAppendDataToCache = new JButton("Cachear datos"); btnReadFromCache = new JButton("Leer datos"); btnDeleteCache = new JButton("Borrar cache"); contentPane.add(lblTime, BorderLayout.NORTH); contentPane.add(buttons, BorderLayout.SOUTH); buttons.add(btnReadFromCache); buttons.add(btnAppendDataToCache); buttons.add(btnDeleteCache); btnAppendDataToCache.addActionListener(this); btnReadFromCache.addActionListener(this); btnDeleteCache.addActionListener(this); this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); this.setSize(new Dimension(400, 200)); this.setContentPane(contentPane); } /* Tareas de liberación de recursos * @see java.awt.Window#dispose() */ public void dispose() { super.dispose(); cache.destroy(); } /** * Intenta leer los datos de la caché. En caso de que no exista los lee de la "base de datos" */ private void readFromCacheTask() { try { customers = (ArrayList ) cache.getFromCache(ALL_CUSTOMER_CACHE_KEY); } catch (NeedsRefreshException e) { // Los datos de la cache no existen o han caducado cache.cancelUpdate(ALL_CUSTOMER_CACHE_KEY); customers = CustomersCtrl.getAll(); } } /** * Añade datos a la cache con un período de validez de 120 segundos */ private void putDataOnCacheTask(){ customers = CustomersCtrl.getAll(); cache.putIncache(ALL_CUSTOMER_cache_KEY, customers, new ExpiresRefreshPolicy(120)); } /** * Vacia la cache */ private void deleteCacheTask() { cache.removeEntry(ALL_CUSTOMER_CACHE_KEY); } /* * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) */ public void actionPerformed(ActionEvent evt) { Object source = evt.getSource(); boolean isError = false; long startTime = System.currentTimeMillis(); try { // Todas las tareas deberían realizarse en un hilo independiente para no // bloquear el hilo principal de repintado if (source == btnReadFromCache){ readFromCacheTask(); } else if (source == btnDeleteCache){ deleteCacheTask(); } else if (source == btnAppendDataToCache){ putDataOnCacheTask(); } } catch (Exception ex){ isError = true; JOptionPane.showMessageDialog(this, ex.getMessage()); } finally { if (! isError){ lblTime.setText("Tiempo en realizar la tarea " + (System.currentTimeMillis() - startTime) + " milisegundos"); } } } /** * Inicia el programa de testeo del API OSCache */ public static void main(String[] args) { JFrame appWin = new OSCacheTestFrame(); appWin.setVisible(true); } }
Si observa la configuración de OSCache para esta aplicación, la caché en disco está habilitada, de manera que en la siguiente imagen se puede observar como OSCache crea y destruye los archivos en donde guarda la información cacheada:
Nota: Al estar habilitada la cache en disco, si cierra la aplicación y la vuelve abrir verá que los datos sigen estando cacheados, es decir, los tiempos de lectura tienden a cero.
(En este caso la cache tiene un tiempo de validez de 120 segundos desde que fué cacheada).
Caso de estudio: Aplicación Web.
OScache, además de ser un sistema de cache de propósito general sobre el cuál podemos cachear objetos. Incluye una librería de Tags y un filtro que proporcionan de forma sencilla la posibilidad de cachear secciones o páginas completas de JSP de nuestras aplicaciones Web.
Si desea más información al respecto, le recomiendo que lea el siguiente tutorial. Ver el tutorial.
Referencias a otros sistemas de caché Open Source
El siguiente enlace se enumeran otros sistemas de caché Open Source:
Open Source cache Solutions in Java
Conclusiones
Bueno como veis es bastante sencillo de integrar en nuestras aplicaciones y las mejoras de rendimiento pueden ser notables.. Eso sí, para la gente nueva en este tema, conviene profundizar más al respecto y elegir bien los puntos estratégicos a cachear.
Carlos García Pérez. Creador de MobileTest, un complemento educativo para los profesores y sus alumnos.
cgpcosmad@gmail.com
¿Alguna razón en concreto para devolver ArrayList en vez de List?
amigo mucha gracias por la Información, me surge una pregunta: Tengo una aplicación de Escritorio en Java SE (Swing) donde se conecta a un DB en un CPANEL de MySql, todo funciona perfecto; pero cuando se cae el Internet, muere la aplicación… con este método puedo hacer que guarde en caché, que conserve los consecutivos, la data etc … y que luego valide si hay internet y procesa a sincronizar ?