Search This Blog

Monday 18 November 2013

Increase Your Application Performance Using EhCache


Ehcache is an open source, standards-based cache for boosting performance, offloading your database, and simplifying scalability. It’s the most widely-used Java-based cache because it’s robust, proven, and full-featured. Ehcache scales from in-process, with one or more nodes, all the way to mixed in-process/out-of-process configurations with terabyte-sized caches. For applications needing a coherent distributed cache, Ehcache uses the open source Terracotta Sever Array.


Download
First, we need to download the
 ehcache-core-2.6.2.jar, slf4j-api-1.6.1.jar and slf4j-jdk14-1.6.1.jar in ehcache-core-2.6.2-distribution.tar.gz from http://ehcache.org/downloads.
Then, add this jar in the librairies folder of your project \war\WEB-INF\ or in your pom.xml (MAVEN);


Configuration ehcache.xml
Create (this file could be copied from downloaded package) an
 ehcache.xml file in the classpath of your project. This file contains a default Cache configuration with an implicit name “default” which is a reserved cache name. This cache will be applied to caches created programmatically (using CacheManager.add(String cacheName)).
So, we will add a sample cache named “myCache1″ wich will contain a maximum in memory of 10000 elements, and will expire an element if it is idle for more than 5 minutes (300 sec) and lives for more than 10 minutes (600 sec). If there are more than 10000 elements it will overflow to the disk cache, which in this configuration will go to wherever java.io.tmp is defined on your system. On a standard Linux system this will be ‘/tmp’, for Windows7 it could be ‘C:\Users\username\AppData\Local\Temp’.
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="ehcache.xsd"
         updateCheck="true" monitoring="autodetect"
         dynamicConfig="true">


    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            statistics="false">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <cache name="myCache1"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>
   
</ehcache>


Use / Utilization
 
To prove the effectiveness of Ehcache, we need create several classes.
  • First, we will create an utilitary class named CacheUtil in order to manipulate and sollicit the Ehcache:
    * Get the cache instance of Ehcache via the method
     getCache. This method could be synchronized. More, to specify the ehcache configuration file, we could use an environment or a VM variable:
·         public static CacheManager cacheMgr = null;
·          
·         private static Ehcache getCache(String cacheName){
·           if(cacheMgr == null){
·                  // We could use an environment or a VM variable
·                  cacheMgr = CacheManager.create("...\\config\\ehcache.xml");
·           }
·                 
·           Ehcache cache = null;
·           if(cacheMgr!=null){
·                  //cache = cacheMgr.addCacheIfAbsent(name);
·                  cache = cacheMgr.getEhcache(cacheName);
·           }
·          
·           return cache;
·         }
* Get data from the cache via the method getListFromCache. This method could be synchronized. In anticipation of its use, this method is waiting a threadName argument.
@SuppressWarnings("unchecked")
public static <T> List<T> getListFromCache(String threadName, String cacheName, String key, CacheCreation<T> cacheCreation){
  List<T> all = new ArrayList<T>();
        
  Ehcache cache = getCache(cacheName);
  Element element = null;
  if(cache!=null){
         element = cache.get(key);
  }
        
  if(element==null){
         System.out.println(threadName+" : CacheUtil.getListFromCache() : the element '"+key+"' has not been found in the cache ---> get the original data.");

         all = cacheCreation.getAll();
         cache.put(new Element(key, all));
         System.out.println(threadName+" : CacheUtil.getListFromCache() : the original data for the element '"+key+"' has been added in the cache.");


  }else{
         System.out.println(threadName+" : CacheUtil.getListFromCache() : the element '"+key+"' has been found in the cache.");

         //all = (List<T>) element.getValue();
         all = (List<T>) element.getObjectValue();
  }
  return all;

}
  • We have also created an abstract class CacheCreation to in anticipation of the use of cache:
·         public abstract class CacheCreation<T> {      
·           public abstract List<T> getAll();    
·         }
  • Then, we will create an class UseCaseClass to use and check the Ehcache containing:
    * main method to create several threads soliciting the Ehcache:
·         public static void main(String[] args) {
·           int nbThreads = 3;
·           ExecutorService execService = Executors.newFixedThreadPool(nbThreads);
·                 
·           // Create several threads which solicit the Ehcache
·           for (int i = 0; i < nbThreads; i++) {
·                  final int indexFinal = i;
·          
·                  execService.submit(new Runnable(){
·                          String threadName= null;
·                          UseCaseClass useCaseClass = null;
·                                 
·                          public void run(){
·                                  try {
·                                         useCaseClass = new UseCaseClass();
·                                         threadName = "thread_"+indexFinal;
·                                         useCaseClass.getAllData1(threadName);
·                                         {
·                                                 int sleepTime = getRandomSleepTime(1000, 5000);
·                                                 System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
·                                                 Thread.currentThread().sleep(sleepTime);
·                                                 System.out.println(threadName+" wakes up");
·                                         }
·                                         useCaseClass.getAllData2(threadName);
·                                         {
·                                                 int sleepTime = getRandomSleepTime(1000, 5000);
·                                                 System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
·                                                 Thread.currentThread().sleep(sleepTime);
·                                                 System.out.println(threadName+" wakes up");
·                                         }
·                                         useCaseClass.getAllData1(threadName);
·                                         useCaseClass.getAllData2(threadName);
·                                         useCaseClass.getAllData1(threadName);
·                                         useCaseClass.getAllData2(threadName);
·                                  } catch (Throwable e) {
·                                         e.printStackTrace();
·                                  }
·                                        
·                          }//end-run
·                                 
·                          private int getRandomSleepTime(int min, int max){
·                                  return min + (int)(Math.random() * ((max - min) + 1));
·                          }
·          
·                  }//end-runnable
·          
·                  );//end-submit
·          
·           }//end-for
·         }
* The Ehcache will be sollicited by 2 methods getAllData1 and getAllData2:
private static final String CACHE_NAME = "myCache1";
 
 
public List<String> getAllData1(final String threadName){
  return CacheUtil.getListFromCache(threadName, CACHE_NAME, "data1", new CacheCreation<String>(){
         @Override
         public List<String> getAll(){
                 System.out.println(threadName+" : UseCaseClass.getAllData1() : the target original method is called to get the values.");
                 List<String> list = new ArrayList<String>();
                 list.add("data1-value1");
                 list.add("data1-value2");
                 list.add("data1-value3");
                 list.add("data1-value4");
                 return list;
         }
  });
}

 
public List<String> getAllData2(final String threadName){
  return CacheUtil.getListFromCache(threadName, CACHE_NAME, "data2", new CacheCreation<String>(){
         @Override
         public List<String> getAll(){
                 System.out.println(threadName+" : UseCaseClass.getAllData2() : the target original method is called to get the values.");
                 List<String> list = new ArrayList<String>();
                 list.add("data2-value1");
                 list.add("data2-value2");
                 list.add("data2-value3");
                 list.add("data2-value4");
                 return list;
         }
  });
}
…So, if we execute the main method:
  • with the following parameters in ehcache.xml (the cache will expire an element in memory if it is idle for more than 5 minutes and lives for more than 10 minutes):
·             <cache name="myCache1"
·                    maxEntriesLocalHeap="10000"
·                    maxEntriesLocalDisk="1000"
·                    eternal="false"
·                    diskSpoolBufferSizeMB="20"
·                    timeToIdleSeconds="300"
·                    timeToLiveSeconds="600"
·                    memoryStoreEvictionPolicy="LFU"
·                    transactionalMode="off">
·                 <persistence strategy="localTempSwap"/>
·             </cache>
…we obtain the below results – at the end, the elements ‘data1′ and ‘data2′ have been found in the cache-:
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 will sleep during 3820ms.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 3252ms.
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_2 will sleep during 2626ms.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_2 will sleep during 3622ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_0 will sleep during 1956ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_1 will sleep during 2747ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
  • with the following parameters in ehcache.xml (the cache will expire an element in memory if it is idle for more than 1 seconds and lives for more than 2 seconds):
·             <cache name="myCache1"
·                    maxEntriesLocalHeap="10000"
·                    maxEntriesLocalDisk="1000"
·                    eternal="false"
·                    diskSpoolBufferSizeMB="20"
·                    timeToIdleSeconds="1"
·                    timeToLiveSeconds="2"
·                    memoryStoreEvictionPolicy="LFU"
·                    transactionalMode="off">
·                 <persistence strategy="localTempSwap"/>
·             </cache>
…we obtain the below results – at the end, the elements ‘data1′ and ‘data2′ have been found in the cache, but they expire frequently-:
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_2 will sleep during 3449ms.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_1 will sleep during 1335ms.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 2558ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_1 will sleep during 3509ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_0 will sleep during 1451ms.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 will sleep during 3111ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.


We change the parameters in ehcache.xml (the cache will expire an element in memory if it is idle for more than 5 minutes and lives for more than 10 minutes):
    <cache name="myCache1"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>
… and we change the method getCache to override the parameters from ehcache.xml:
Ehcache cache = null;
if(cacheMgr!=null){
        //cache = cacheMgr.addCacheIfAbsent(name);
        cache = cacheMgr.getEhcache(cacheName);

        //It is possible to override the parameters from ehcache.xml
        cache.getCacheConfiguration().setTimeToIdleSeconds(1);
        cache.getCacheConfiguration().setTimeToLiveSeconds(2);
}
…we obtain the below results – at the end, the elements ‘data1′ and ‘data2′ have been found in the cache, but they expire frequently-:
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 3295ms.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_1 will sleep during 1603ms.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_2 will sleep during 4515ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_1 will sleep during 4186ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_0 will sleep during 1049ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 will sleep during 4864ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data1' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the original data for the element 'data2' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the element 'data2' has been found in the cache.



counters