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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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.ArrayList<String> getAll(){ java.util.ArrayList<String> customers = new java.util.ArrayList<String>(); 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é.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
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 ArrayList<String> customers; // 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 <String>) 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 ?