简体   繁体   中英

EHcache simple example with time-to-live

I need a simple cache for storing tuples in memory with a certain time-to-live. I couldn't find a way to do that on EHcache website, which contains mostly complex usage scenarios. Can anyone help me out?

PS I don't use Spring.

Ehcache 2.x

Programmatic

CacheManager cacheManager = initCacheManager();
CacheConfiguration cacheConfiguration = new CacheConfiguration().name("myCache")
        .maxEntriesLocalHeap(100)
        .timeToLiveSeconds(20);
cacheManager.addCache(new Cache(cacheConfiguration));

XML

<cache name="myCache"
       maxEntriesLocalHeap="100"
       timeToLiveSeconds="20"/>

Override per Element

Ehcache 2.x allows your to override expiry settings per Element :

Element element = new Element("key", "value");
element.setTimeToLive(10);
cache.put(element);

Ehcache 3.x

Programmatic

CacheManager cacheManager = initCacheManager();
CacheConfigurationBuilder<Long, String> configuration = 
    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class, ResourcePoolsBuilder
      .heap(100))
      .withExpiry(Expirations.timeToLiveExpiration(new Duration(20, TimeUnit.SECONDS)));
cacheManager.createCache("myCache", configuration);

In Ehcache 3, the builder is immutable and can be safely shared to create multiple caches from a similar configuration. And the code will be more compact if you use static imports, which I did not do here to ease pasting this snippet in an IDE.

XML

<cache alias="myCache">
  <expiry>
    <ttl unit="seconds">20</ttl>
  </expiry>
  <heap>100</heap>
</cache>

Override through custom Expiry

in Ehcache 3.x, Expiry is an interface which users can implement:

public interface Expiry<K, V> {
  Duration getExpiryForCreation(K key, V value);
  Duration getExpiryForAccess(K key, ValueSupplier<? extends V> value);
  Duration getExpiryForUpdate(K key, ValueSupplier<? extends V> oldValue, V newValue);
}

time-to-live matches the getExpiryForCreation invocation, which will receive the key and value of the mapping, allowing to implement different expirations depending on the mapping itself.

Ehcache 3.x solution to define expiry per element on runtime

This solution was tested as working fine on Ehcache 3.8.1, but the code is for sure valid for any Ehcache 3.x

First create a class to keep a generic, in fact any object you are able pass to cache:

    import java.io.Serializable;
    import java.time.Duration;

    public class EhcacheValue<T> implements Serializable {

        private static final long serialVersionUID = 1L;

        private T object;

        private long timeToLive;

        public EhcacheValue(T theObject, Duration theDuration) {
            object = theObject;
            timeToLive = theDuration.getSeconds();
        }

        public Duration getTimeToLiveDuration() {
            return Duration.ofSeconds(timeToLive);
        }

        public T getObject() {
            return object;
        }

        public void setObject(T theObject) {
            object = theObject;
        }

        public long getTimeToLive() {
            return timeToLive;
        }

        public void setTimeToLive(long theTimeToLive) {
            timeToLive = theTimeToLive;
        }

    }

Then create a custom Expiry class implementing the Ehcache 3.x interface ExpiryPolicy :

import java.time.Duration;

import java.util.function.Supplier;

import org.ehcache.expiry.ExpiryPolicy;

public class CustomExpiryPolicy<K, V extends EhcacheValue<?>> implements ExpiryPolicy<K, V> {

    public CustomExpiryPolicy() {

    }

    @Override
    public Duration getExpiryForCreation(K theKey, V theValue) {
       return theValue.getTimeToLiveDuration();
    }

    @Override
    public Duration getExpiryForAccess(K theKey, Supplier<? extends V> theValue) {
        return null;
    }

    @Override
    public Duration getExpiryForUpdate(K theKey, Supplier<? extends V> theOldValue, V theNewValue) {
         return theNewValue.getTimeToLiveDuration();
       }
    }

Then you have the normal code, but with a few important lines on the way :

public class TestEhCache {

    private static final String CACHE_TIER_HEAP = "OnHeap";

    public static void main(String[] args) {

        StatisticsService statisticsService = new DefaultStatisticsService();

        myCacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .using(statisticsService)
                .build();

        //we init it here, not with boolean in build()
        myCacheManager.init();

        // Beware, the items are not expiring automatically, there are more mechanism in place for checking the expiration
        // For instance the expiration is checked at get time
        // for more details see the documentation from the version 2.8 
        // here: https://www.ehcache.org/documentation/2.8/apis/cache-event-listeners.html
        // Unfortunately for the version 3.x doesn't exist a detailed depiction, but in main the procedure is the same
        //
        // Ehcache 2.8 documentation:
        // Elements are checked for expiry in Ehcache at the following times:
        //
        // When a get request is made
        // When an element is spooled to the diskStore in accordance with a MemoryStore eviction policy
        // In the DiskStore when the expiry thread runs, which by default is
        // net.sf.ehcache.Cache#DEFAULT_EXPIRY_THREAD_INTERVAL_SECONDS

        CustomExpiryPolicy<String,EhcacheValue<?>> expiryPolicy = new CustomExpiryPolicy<String,EhcacheValue<?>>();

        ResourcePools resourcePools = ResourcePoolsBuilder.newResourcePoolsBuilder().heap(2000L, EntryUnit.ENTRIES).build();

        @SuppressWarnings("unchecked")
        Class<EhcacheValue<?>> myEhcacheValue = (Class<EhcacheValue<?>>)(Class<?>)EhcacheValue.class;

        CacheConfiguration<String,EhcacheValue<?>> cacheConfiguration = CacheConfigurationBuilder
                .newCacheConfigurationBuilder(String.class, myEhcacheValue, resourcePools)
                .withExpiry(expiryPolicy)
                //.withService(new StoreStatisticsConfiguration(true)) // explicitly enable statistics, it seems is not needed
                .build();

        myCache = myCacheManager.createCache("myCache", cacheConfiguration);

        myCacheStatistics = statisticsService.getCacheStatistics("myCache");

        long cacheEntriesNr = myCacheStatistics.getTierStatistics().get(CACHE_TIER_HEAP).getMappings();

        //nb element in heap tier
        long sizeEntriesByStatistics = myCacheStatistics.getTierStatistics().get("OnHeap").getMappings();
        //size of the tier in memory, when you set memory limits, not in this case
        //long sizeBytesByStatistics = myCacheStatistics.getTierStatistics().get("OnHeap").getOccupiedByteSize();

        long getsCnt = myCacheStatistics.getCacheGets();
        long putsCnt = myCacheStatistics.getCachePuts();
        long removalsCnt = myCacheStatistics.getCacheRemovals();
        long missesCnt = myCacheStatistics.getCacheMisses();
        long evictionsCnt = myCacheStatistics.getCacheEvictions();
        long crtExpiredCnt = myCacheStatistics.getCacheExpirations();

        System.out.println("getsCnt = "+getsCnt);
        System.out.println("putsCnt = "+putsCnt);
        System.out.println("removalsCnt = "+removalsCnt);
        System.out.println("missesCnt = "+missesCnt);
        System.out.println("evictionsCnt = "+evictionsCnt);
        System.out.println("expiredCnt = "+expiredCnt);
        System.out.println("hitPercentage = "+hitPercentage);
        System.out.println("missPercentage = "+missPercentage);
        System.out.println("Computed number of items in cache = "+(putsCnt-removalsCnt-expiredCnt-evictionsCnt));

    }

    @SuppressWarnings("unchecked")
    // we are aware about the unchecked cast - it is a desired behavior. 
    // Cache can store objects of various types, so compile time type safety cannot be achieved easily.
    // We'll get runtime ClassCastException if cache is used incorrectly.
    public static <T> T get(String key) {
        EhcacheValue<T> ehcacheValue = (EhcacheValue<T>)myCache.get(key);
        return (T) (ehcacheValue!=null?ehcacheValue.getObject():null);
    }

    // we are aware about the unchecked cast - it is a desired behavior. 
    // Cache can store objects of various types, so compile time type safety cannot be achieved easily.
    // We'll get runtime ClassCastException if cache is used incorrectly.
    public static <T extends Object> T put(String key, T value, int timeToLiveInSeconds) {

        if (key == null) {
            throw new AssertionError("Key must not be null!");
        }

        if (value != null) {
            EhcacheValue<T> ehcacheValue = new EhcacheValue<T>(value, Duration.of(60, ChronoUnit.SECONDS));
            myCache.put(key, ehcacheValue);
            return value;
        } else {
            return null;
        }
    }
}

As for EhCache version 3.3.1, groupId:org.ehcache,artifactId:ehcache , the following works!

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder().withCache("AllCache",
        CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,String.class,
            ResourcePoolsBuilder.heap(100))
            .withExpiry(Expirations.timeToLiveExpiration(new Duration(86400, TimeUnit.SECONDS)))
            .build()).build(true);


Cache<String, String> allCache = cacheManager.getCache("AllCache", String.class, String.class);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM