Spring Cache (with Redis | EhCache | Memcahed)
24 Oct 2018
0. The very first is pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.sergpank.test</groupId>
<artifactId>spring.playground</artifactId>
<version>0.1</version>
<name>spring.playground</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!--REDIS-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.15.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!--EHCACHE-->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.8.3</version>
</dependency>
<!--MEMCACHED-->
<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>spring-cache</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>xmemcached-provider</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>com.google.code.simple-spring-memcached</groupId>
<artifactId>spymemcached-provider</artifactId>
<version>3.1.0</version>
</dependency>
<!--LOGGING-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
</project>
1. And here is app-context.xml
(key and value serializers are not necesary but String are more readable than default bytearrays):
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd">
<context:component-scan base-package="com.sergpank.test" />
<cache:annotation-driven />
<!-- REDIS cache configuration begin -->
<bean id='jedisConnectionFactory'
class='org.springframework.data.redis.connection.jedis.JedisConnectionFactory'
/>
<bean id="keySerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"
/>
<bean id="valueSerializer"
class="org.springframework.data.redis.serializer.StringRedisSerializer"
/>
<bean id='redisTemplate'
class='org.springframework.data.redis.core.RedisTemplate'
p:connection-factory-ref='jedisConnectionFactory'
p:keySerializer-ref='keySerializer'
p:valueSerializer-ref='valueSerializer'
/>
<bean id='cacheManager'
class='org.springframework.data.redis.cache.RedisCacheManager'
c:redisOperations-ref='redisTemplate'
p:loadRemoteCachesOnStartup="true"
p:defaultExpiration="1200"
/>
<!-- REDIS cache configuration end -->
<!-- EHCACHE cache configuration begin -->
<!--<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">-->
<!--<property name="configLocation" value="classpath:ehcache.xml"/>-->
<!--<property name="shared" value="false"/>-->
<!--</bean>-->
<!--<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">-->
<!--<property name="cacheManager" ref="ehcache"/>-->
<!--</bean>-->
<!--EHCACHE cache configuration end -->
<!-- MEMCACHED cache configuration begin -->
<!--<bean name="cacheManager" class="com.google.code.ssm.spring.SSMCacheManager">-->
<!--<property name="caches">-->
<!--<set>-->
<!--<bean class="com.google.code.ssm.spring.SSMCache">-->
<!--<constructor-arg name="cache" index="0" ref="testCache" />-->
<!--<!– 20 minutes –>-->
<!--<constructor-arg name="expiration" index="1" value="1200" />-->
<!--<!– @CacheEvict(..., "allEntries" = true) doesn't work –>-->
<!--<constructor-arg name="allowClear" index="2" value="false" />-->
<!--</bean>-->
<!--</set>-->
<!--</property>-->
<!--</bean>-->
<!--<bean name="testCache" class="com.google.code.ssm.CacheFactory">-->
<!--<property name="cacheName" value="testCache" />-->
<!--<property name="cacheClientFactory">-->
<!--<bean name="cacheClientFactory"-->
<!--class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl" />-->
<!--</property>-->
<!--<property name="addressProvider">-->
<!--<bean class="com.google.code.ssm.config.DefaultAddressProvider">-->
<!--<property name="address" value="127.0.0.1:11211" />-->
<!--</bean>-->
<!--</property>-->
<!--<property name="configuration">-->
<!--<bean class="com.google.code.ssm.providers.CacheConfiguration">-->
<!--<property name="consistentHashing" value="true" />-->
<!--</bean>-->
<!--</property>-->
<!--</bean>-->
<!-- MEMCACHED cache configuration end -->
</beans>
2. Now lets create a Cached method:
package com.sergpank.test;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
public class CacheTest
{
private static final Logger log = LogManager.getLogger(CacheTest.class);
@Cacheable(value = "testCache", key = "'testCache-' + #root.args")
public String testCache(int val)
{
log.info("Not cached - " + val);
return Integer.toString(val);
}
}
3. And a simple tester for all that stuff:
package com.sergpank.test;
import java.util.stream.IntStream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
private static final Logger log = LogManager.getLogger(App.class);
public static void main(String[] args)
{
log.info("Test !!!");
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
CacheTest test = ctx.getBean(CacheTest.class);
log.info("Not cached! ...");
IntStream.rangeClosed(0, 10).forEach(i -> log.info(test.testCache(i)));
log.info("CACHED! ...");
IntStream.rangeClosed(0, 10).forEach(i -> log.info(test.testCache(i)));
System.exit(0);
}
}
4. Don’t forget about configuration files:
ehcache.xml
:
<ehcache>
<diskStore path="java.io.tmpdir"/>
<cache name="testCache"
maxElementsInMemory="100000"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
log4j2.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration packages="com.opera.fnd.util">
<Appenders>
<Console name="stdout" target="SYSTEM_OUT">
<PatternLayout pattern="%d{ABSOLUTE} %5p %c{1}:%L %m%n" />
</Console>
</Appenders>
<Loggers>
<AsyncRoot level="info">
<AppenderRef ref="stdout" />
</AsyncRoot>
</Loggers>
</Configuration>
5. Now you can comment out necessary cache provider and check if it works
Expected output:
15:46:13,573 INFO App: Test !!!
15:46:14,336 INFO App: Not cached! ...
15:46:14,411 INFO CacheTest: Not cached - 0
15:46:14,424 INFO App: 0
15:46:14,425 INFO CacheTest: Not cached - 1
15:46:14,425 INFO App: 1
15:46:14,426 INFO CacheTest: Not cached - 2
15:46:14,427 INFO App: 2
15:46:14,427 INFO CacheTest: Not cached - 3
15:46:14,428 INFO App: 3
15:46:14,428 INFO CacheTest: Not cached - 4
15:46:14,429 INFO App: 4
15:46:14,430 INFO CacheTest: Not cached - 5
15:46:14,430 INFO App: 5
15:46:14,431 INFO CacheTest: Not cached - 6
15:46:14,431 INFO App: 6
15:46:14,432 INFO CacheTest: Not cached - 7
15:46:14,433 INFO App: 7
15:46:14,434 INFO CacheTest: Not cached - 8
15:46:14,435 INFO App: 8
15:46:14,435 INFO CacheTest: Not cached - 9
15:46:14,436 INFO App: 9
15:46:14,436 INFO CacheTest: Not cached - 10
15:46:14,437 INFO App: 10
15:46:14,437 INFO App: CACHED! ...
15:46:14,440 INFO App: 0
15:46:14,440 INFO App: 1
15:46:14,441 INFO App: 2
15:46:14,442 INFO App: 3
15:46:14,442 INFO App: 4
15:46:14,443 INFO App: 5
15:46:14,443 INFO App: 6
15:46:14,444 INFO App: 7
15:46:14,444 INFO App: 8
15:46:14,445 INFO App: 9
15:46:14,445 INFO App: 10
Additinaly you need to have Redis and Memcached installed and running.
No need to install EhCache as it runs in JVM with project itself.
Redis hints:
redis-server & -> start redis
redis-cli -> redis commmand-line-interface
keys * -> show all keys
get <key_name> -> get value of some key
flushall -> clear redis cache
Memcached hints:
memcached & -> start memecached
telnet 127.0.0.1 11211 -> connect to local memcached instance
stats items -> show brief memecached stats
stats cachedump <slab_id> <limit> -> show <limit> items from <slab_id>
quit -> disconnect from memcached cli
stats items
STAT items:1:number 11 // the first number after "items:" is slab_id
STAT items:1:number_hot 0
STAT items:1:number_warm 0
STAT items:1:number_cold 11
...
END
stats cachedump 1 3
ITEM testCache-10 [2 b; 1553005477 s]
ITEM testCache-9 [1 b; 1553005477 s]
ITEM testCache-8 [1 b; 1553005477 s]
END