├── README.md ├── pom.xml └── src ├── main └── java │ ├── org │ └── springframework │ │ └── data │ │ └── redis │ │ └── cache │ │ ├── ExpirableRedisCacheWriter.java │ │ └── ExpireRedisCacheManager.java │ └── roman │ └── springframework │ └── cache │ ├── annotation │ ├── Expire.java │ └── SpringExpireCacheAnnotationParser.java │ ├── interceptor │ ├── CacheExpireOperation.java │ ├── CacheExpireOperationManager.java │ ├── ExpireCahceInterceptor.java │ └── SpringExpireCacheSource.java │ └── redis │ ├── ExpirableRedisCacheWriter.java │ └── ExpireRedisCacheManager.java └── test ├── java └── org │ └── springframework │ └── data │ └── redis │ └── cache │ ├── ApplicationTests.java │ ├── CacheConfig.java │ └── UserService.java └── resources └── application.yaml /README.md: -------------------------------------------------------------------------------- 1 | # spring-cache-redis-expire 2 | spring redis框架过期时间注解支持 3 | --- 4 | 5 | 默认spring redis注解缓存不支持过期时间定义,增加Expire注解,支持过期单独key定义过期时间; 6 | 实现逻辑是在ExpireCahceInterceptor.execute时,使用Threadlocal设置过期时间,再在ExpirableRedisCacheWriter.put时获取该时间。 7 | 8 | 9 | ## example 10 | 11 | 单元测试使用spring boot 2.0测试,spring boot缓存配置参考CacheConfig,测试类ApplicationTests 12 | 13 | 测试代码: 14 | 15 | ``` 16 | @Expire(10000) 17 | @Cacheable(value = "user", key = "'ids'") 18 | public List listUserIds() { 19 | logger.info("user ids from db"); 20 | return Arrays.asList(1l, 2l, 3l); 21 | } 22 | ``` 23 | 24 | 测试结果: 25 | 26 | ``` 27 | 2018-10-09 15:04:25.963 INFO 36380 --- [ main] Service$$EnhancerBySpringCGLIB$$261954db : user ids from db 28 | 2018-10-09 15:04:25.988 INFO 36380 --- [ main] o.s.data.redis.cache.ApplicationTests : cache expire ttl: 10 29 | ``` 30 | 31 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | roman 8 | roman.spring.cache.redis 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | org.springframework.data 22 | spring-data-redis 23 | 2.0.10.RELEASE 24 | 25 | 26 | 27 | 28 | io.lettuce 29 | lettuce-core 30 | 5.1.0.RELEASE 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-test 38 | 2.0.5.RELEASE 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.apache.maven.plugins 48 | maven-compiler-plugin 49 | 50 | 8 51 | 8 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/data/redis/cache/ExpirableRedisCacheWriter.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.redis.cache; 2 | 3 | import org.springframework.data.redis.connection.RedisConnectionFactory; 4 | import org.springframework.lang.Nullable; 5 | import roman.springframework.cache.interceptor.CacheExpireOperation; 6 | import roman.springframework.cache.interceptor.CacheExpireOperationManager; 7 | 8 | import java.time.Duration; 9 | 10 | /** 11 | * 12 | * @Auther: romanluo 13 | * @Date: 2018/10/8 14 | */ 15 | public class ExpirableRedisCacheWriter implements RedisCacheWriter { 16 | 17 | private final RedisCacheWriter delegateRedisCacheWriter; 18 | 19 | ExpirableRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 20 | this(new DefaultRedisCacheWriter(connectionFactory, Duration.ZERO)); 21 | } 22 | 23 | /** 24 | * @param connectionFactory must not be {@literal null}. 25 | * @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO} 26 | * to disable locking. 27 | */ 28 | ExpirableRedisCacheWriter(final RedisConnectionFactory connectionFactory, final Duration sleepTime) { 29 | this(new DefaultRedisCacheWriter(connectionFactory, sleepTime)); 30 | } 31 | 32 | public ExpirableRedisCacheWriter(final RedisCacheWriter delegateRedisCacheWriter) { 33 | this.delegateRedisCacheWriter = delegateRedisCacheWriter; 34 | } 35 | 36 | public static RedisCacheWriter nonLockingRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 37 | return RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); 38 | } 39 | 40 | public static RedisCacheWriter lockingRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 41 | return RedisCacheWriter.lockingRedisCacheWriter(connectionFactory); 42 | } 43 | 44 | @Override 45 | public void put(final String name, final byte[] key, final byte[] value, @Nullable final Duration ttl) { 46 | final Duration duration = getDuration(ttl); 47 | delegateRedisCacheWriter.put(name, key, value, duration); 48 | } 49 | 50 | @Override 51 | @Nullable 52 | public byte[] get(final String name, final byte[] key) { 53 | return delegateRedisCacheWriter.get(name, key); 54 | } 55 | 56 | @Override 57 | @Nullable 58 | public byte[] putIfAbsent(final String name, final byte[] key, final byte[] value, @Nullable final Duration ttl) { 59 | final Duration duration = getDuration(ttl); 60 | return delegateRedisCacheWriter.putIfAbsent(name, key, value, duration); 61 | } 62 | 63 | @Override 64 | public void remove(final String name, final byte[] key) { 65 | delegateRedisCacheWriter.remove(name, key); 66 | } 67 | 68 | @Override 69 | public void clean(final String name, final byte[] pattern) { 70 | delegateRedisCacheWriter.clean(name, pattern); 71 | } 72 | 73 | private Duration getDuration(@Nullable final Duration ttl) { 74 | Duration duration = ttl; 75 | final CacheExpireOperation cacheExpireOperation = CacheExpireOperationManager.get(); 76 | if (cacheExpireOperation != null) 77 | duration = Duration.ofMillis(cacheExpireOperation.getTtl()); 78 | return duration; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/data/redis/cache/ExpireRedisCacheManager.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.redis.cache; 2 | 3 | import org.springframework.cache.Cache; 4 | import org.springframework.cache.CacheManager; 5 | import org.springframework.data.redis.connection.RedisConnectionFactory; 6 | import org.springframework.lang.Nullable; 7 | 8 | import java.util.Collection; 9 | import java.util.Map; 10 | 11 | /** 12 | * 13 | * @Auther: romanluo 14 | * @Date: 2018/10/8 15 | */ 16 | public class ExpireRedisCacheManager implements CacheManager { 17 | 18 | private final RedisCacheManager delegateRedisCacheManager; 19 | 20 | public ExpireRedisCacheManager(final Map cacheConfigurations, 21 | final RedisConnectionFactory connectionFactory) { 22 | final RedisCacheWriter redisCacheWriter = new ExpirableRedisCacheWriter(connectionFactory); 23 | final RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); 24 | delegateRedisCacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration, 25 | cacheConfigurations); 26 | } 27 | 28 | @Override 29 | @Nullable 30 | public Cache getCache(final String name) { 31 | return delegateRedisCacheManager.getCache(name); 32 | } 33 | 34 | @Override 35 | public Collection getCacheNames() { 36 | return delegateRedisCacheManager.getCacheNames(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/annotation/Expire.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.annotation; 2 | 3 | import org.springframework.core.annotation.AliasFor; 4 | 5 | import java.lang.annotation.*; 6 | 7 | /** 8 | * 9 | * @Auther: romanluo 10 | * @Date: 2018/10/8 11 | */ 12 | @Target({ElementType.METHOD}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Inherited 15 | @Documented 16 | public @interface Expire { 17 | 18 | @AliasFor("value") long ttl() default 60000; 19 | 20 | @AliasFor("ttl") long value() default 60000; 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/annotation/SpringExpireCacheAnnotationParser.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.annotation; 2 | 3 | import org.springframework.cache.annotation.CachePut; 4 | import org.springframework.cache.annotation.Cacheable; 5 | import org.springframework.core.annotation.AnnotationUtils; 6 | import org.springframework.lang.Nullable; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * 12 | * @Auther: romanluo 13 | * @Date: 2018/10/8 14 | */ 15 | public class SpringExpireCacheAnnotationParser { 16 | 17 | @Nullable 18 | public Expire parseExpireAnnotations(final Method method) { 19 | if (method.getAnnotation(Cacheable.class) == null && method.getAnnotation(CachePut.class) == null) { 20 | return null; 21 | } 22 | return AnnotationUtils.findAnnotation(method, Expire.class); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/interceptor/CacheExpireOperation.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.interceptor; 2 | 3 | /** 4 | * 5 | * @Auther: romanluo 6 | * @Date: 2018/10/8 7 | */ 8 | public class CacheExpireOperation { 9 | 10 | private final long ttl; 11 | 12 | public CacheExpireOperation(final long ttl) { 13 | this.ttl = ttl; 14 | } 15 | 16 | public long getTtl() { 17 | return ttl; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/interceptor/CacheExpireOperationManager.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.interceptor; 2 | 3 | /** 4 | * 5 | * @Auther: romanluo 6 | * @Date: 2018/10/8 7 | */ 8 | public class CacheExpireOperationManager { 9 | 10 | private static final ThreadLocal cacheExpireOperationManager = new ThreadLocal<>(); 11 | 12 | public static CacheExpireOperation get() { 13 | return cacheExpireOperationManager.get(); 14 | } 15 | 16 | public static void set(final CacheExpireOperation value) { 17 | cacheExpireOperationManager.set(value); 18 | } 19 | 20 | public static void remove() { 21 | cacheExpireOperationManager.remove(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/interceptor/ExpireCahceInterceptor.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.interceptor; 2 | 3 | import org.springframework.aop.framework.AopProxyUtils; 4 | import org.springframework.cache.interceptor.CacheInterceptor; 5 | import org.springframework.cache.interceptor.CacheOperationInvoker; 6 | import org.springframework.lang.Nullable; 7 | 8 | import java.lang.reflect.Method; 9 | 10 | /** 11 | * 12 | * @Auther: romanluo 13 | * @Date: 2018/10/8 14 | */ 15 | public class ExpireCahceInterceptor extends CacheInterceptor { 16 | 17 | private final SpringExpireCacheSource springExpireCacheSource = new SpringExpireCacheSource(); 18 | 19 | @Nullable 20 | @Override 21 | protected Object execute(final CacheOperationInvoker invoker, final Object target, final Method method, 22 | final Object[] args) { 23 | final Class targetClass = AopProxyUtils.ultimateTargetClass(target); 24 | final CacheExpireOperation expireOperation = springExpireCacheSource.getExpireOperation(method, targetClass); 25 | if (targetClass != null) { 26 | CacheExpireOperationManager.set(expireOperation); 27 | } 28 | try { 29 | return super.execute(invoker, target, method, args); 30 | } finally { 31 | CacheExpireOperationManager.remove(); 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/interceptor/SpringExpireCacheSource.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.interceptor; 2 | 3 | import org.springframework.core.MethodClassKey; 4 | import roman.springframework.cache.annotation.Expire; 5 | import roman.springframework.cache.annotation.SpringExpireCacheAnnotationParser; 6 | 7 | import java.lang.reflect.Method; 8 | import java.util.Map; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | /** 12 | * 13 | * @Auther: romanluo 14 | * @Date: 2018/10/8 15 | */ 16 | public class SpringExpireCacheSource { 17 | 18 | private final Map attributeCache = new ConcurrentHashMap<>(); 19 | 20 | private final SpringExpireCacheAnnotationParser springExpireCacheAnnotationParser = new SpringExpireCacheAnnotationParser(); 21 | 22 | public CacheExpireOperation getExpireOperation(final Method method, final Class targetClass) { 23 | if (method.getDeclaringClass() == Object.class) { 24 | return null; 25 | } 26 | 27 | final Object cacheKey = new MethodClassKey(method, targetClass); 28 | final CacheExpireOperation cached = attributeCache.get(cacheKey); 29 | 30 | if (cached != null) { 31 | return cached; 32 | } else { 33 | final Expire expire = springExpireCacheAnnotationParser.parseExpireAnnotations(method); 34 | final CacheExpireOperation cacheOps = new CacheExpireOperation(expire.ttl()); 35 | if (cacheOps != null) { 36 | attributeCache.put(cacheKey, cacheOps); 37 | } else { 38 | attributeCache.put(cacheKey, null); 39 | } 40 | return cacheOps; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/redis/ExpirableRedisCacheWriter.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.redis; 2 | 3 | import org.springframework.data.redis.cache.RedisCacheWriter; 4 | import org.springframework.data.redis.connection.RedisConnectionFactory; 5 | import org.springframework.lang.Nullable; 6 | import roman.springframework.cache.interceptor.CacheExpireOperation; 7 | import roman.springframework.cache.interceptor.CacheExpireOperationManager; 8 | 9 | import java.time.Duration; 10 | 11 | /** 12 | * 13 | * @Auther: romanluo 14 | * @Date: 2018/10/8 15 | */ 16 | public class ExpirableRedisCacheWriter implements RedisCacheWriter { 17 | 18 | private final RedisCacheWriter delegateRedisCacheWriter; 19 | 20 | ExpirableRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 21 | this(RedisCacheWriter.lockingRedisCacheWriter(connectionFactory)); 22 | } 23 | 24 | public ExpirableRedisCacheWriter(final RedisCacheWriter delegateRedisCacheWriter) { 25 | this.delegateRedisCacheWriter = delegateRedisCacheWriter; 26 | } 27 | 28 | public static RedisCacheWriter nonLockingRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 29 | return RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); 30 | } 31 | 32 | public static RedisCacheWriter lockingRedisCacheWriter(final RedisConnectionFactory connectionFactory) { 33 | return RedisCacheWriter.lockingRedisCacheWriter(connectionFactory); 34 | } 35 | 36 | @Override 37 | public void put(final String name, final byte[] key, final byte[] value, @Nullable final Duration ttl) { 38 | final Duration duration = getDuration(ttl); 39 | delegateRedisCacheWriter.put(name, key, value, duration); 40 | } 41 | 42 | @Override 43 | @Nullable 44 | public byte[] get(final String name, final byte[] key) { 45 | return delegateRedisCacheWriter.get(name, key); 46 | } 47 | 48 | @Override 49 | @Nullable 50 | public byte[] putIfAbsent(final String name, final byte[] key, final byte[] value, @Nullable final Duration ttl) { 51 | final Duration duration = getDuration(ttl); 52 | return delegateRedisCacheWriter.putIfAbsent(name, key, value, duration); 53 | } 54 | 55 | @Override 56 | public void remove(final String name, final byte[] key) { 57 | delegateRedisCacheWriter.remove(name, key); 58 | } 59 | 60 | @Override 61 | public void clean(final String name, final byte[] pattern) { 62 | delegateRedisCacheWriter.clean(name, pattern); 63 | } 64 | 65 | private Duration getDuration(@Nullable final Duration ttl) { 66 | Duration duration = ttl; 67 | final CacheExpireOperation cacheExpireOperation = CacheExpireOperationManager.get(); 68 | if (cacheExpireOperation != null) 69 | duration = Duration.ofMillis(cacheExpireOperation.getTtl()); 70 | return duration; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/roman/springframework/cache/redis/ExpireRedisCacheManager.java: -------------------------------------------------------------------------------- 1 | package roman.springframework.cache.redis; 2 | 3 | import org.springframework.cache.Cache; 4 | import org.springframework.cache.CacheManager; 5 | import org.springframework.data.redis.cache.RedisCacheConfiguration; 6 | import org.springframework.data.redis.cache.RedisCacheManager; 7 | import org.springframework.data.redis.cache.RedisCacheWriter; 8 | import org.springframework.data.redis.connection.RedisConnectionFactory; 9 | import org.springframework.lang.Nullable; 10 | 11 | import java.util.Collection; 12 | import java.util.Map; 13 | 14 | /** 15 | * 16 | * @Auther: romanluo 17 | * @Date: 2018/10/8 18 | */ 19 | public class ExpireRedisCacheManager implements CacheManager { 20 | 21 | private final RedisCacheManager delegateRedisCacheManager; 22 | 23 | public ExpireRedisCacheManager(final Map cacheConfigurations, 24 | final RedisConnectionFactory connectionFactory) { 25 | final RedisCacheWriter redisCacheWriter = new ExpirableRedisCacheWriter(connectionFactory); 26 | final RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); 27 | delegateRedisCacheManager = new RedisCacheManager(redisCacheWriter, redisCacheConfiguration, 28 | cacheConfigurations); 29 | } 30 | 31 | @Override 32 | @Nullable 33 | public Cache getCache(final String name) { 34 | return delegateRedisCacheManager.getCache(name); 35 | } 36 | 37 | @Override 38 | public Collection getCacheNames() { 39 | return delegateRedisCacheManager.getCacheNames(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/data/redis/cache/ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.redis.cache; 2 | 3 | /** 4 | * 5 | * @Auther: romanluo 6 | * @Date: 2018/10/8 7 | */ 8 | 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | import org.springframework.data.redis.core.RedisTemplate; 15 | import org.springframework.data.redis.serializer.StringRedisSerializer; 16 | import org.springframework.test.context.junit4.SpringRunner; 17 | 18 | import javax.annotation.Resource; 19 | 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest(classes = {UserService.class, CacheConfig.class}) 22 | public class ApplicationTests { 23 | 24 | private final Logger logger = LoggerFactory.getLogger(getClass()); 25 | 26 | @Resource 27 | private UserService userService; 28 | 29 | @Resource 30 | private RedisTemplate redisTemplate; 31 | 32 | @Test 33 | public void testCache() { 34 | userService.listUserIds(); 35 | redisTemplate.setKeySerializer(new StringRedisSerializer()); 36 | logger.info("cache expire ttl: {}", redisTemplate.getExpire("user::ids")); 37 | userService.listUserIds(); 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/data/redis/cache/CacheConfig.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.redis.cache; 2 | 3 | import org.springframework.beans.factory.ObjectProvider; 4 | import org.springframework.cache.annotation.EnableCaching; 5 | import org.springframework.cache.interceptor.BeanFactoryCacheOperationSourceAdvisor; 6 | import org.springframework.cache.interceptor.CacheInterceptor; 7 | import org.springframework.cache.interceptor.CacheOperationSource; 8 | import org.springframework.cache.interceptor.ExpireCahceInterceptor; 9 | import org.springframework.cache.redis.ExpirableRedisCacheWriter; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.data.redis.connection.RedisConnectionFactory; 12 | 13 | /** 14 | * 15 | * @Auther: romanluo 16 | * @Date: 2018/10/8 17 | */ 18 | @EnableCaching 19 | public class CacheConfig { 20 | 21 | /*cache config begin*/ 22 | @Bean 23 | public RedisCacheManager cacheManager(final RedisConnectionFactory redisConnectionFactory, 24 | final ObjectProvider redisCacheConfiguration) { 25 | final RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager 26 | .builder(new ExpirableRedisCacheWriter(redisConnectionFactory)); 27 | final RedisCacheConfiguration defaultRedisCacheConfiguration = redisCacheConfiguration.getIfAvailable(); 28 | if (defaultRedisCacheConfiguration != null) 29 | builder.cacheDefaults(defaultRedisCacheConfiguration); 30 | return builder.build(); 31 | } 32 | 33 | @Bean 34 | public CacheInterceptor expireCahceInterceptor( 35 | final BeanFactoryCacheOperationSourceAdvisor cacheOperationSourceAdvisor, 36 | final CacheOperationSource cacheOperationSource) { 37 | final ExpireCahceInterceptor expireCahceInterceptor = new ExpireCahceInterceptor(); 38 | expireCahceInterceptor.setCacheOperationSources(cacheOperationSource); 39 | cacheOperationSourceAdvisor.setAdvice(expireCahceInterceptor); 40 | return expireCahceInterceptor; 41 | } 42 | /*cache config finish*/ 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/org/springframework/data/redis/cache/UserService.java: -------------------------------------------------------------------------------- 1 | package org.springframework.data.redis.cache; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.cache.annotation.Cacheable; 7 | import org.springframework.cache.annotation.Expire; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | /** 13 | * 14 | * @Auther: romanluo 15 | * @Date: 2018/10/9 16 | */ 17 | @SpringBootApplication 18 | public class UserService { 19 | 20 | private final Logger logger = LoggerFactory.getLogger(getClass()); 21 | 22 | @Expire(10000) 23 | @Cacheable(value = "user", key = "'ids'") 24 | public List listUserIds() { 25 | logger.info("user ids from db"); 26 | return Arrays.asList(1l, 2l, 3l); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | 2 | spring: 3 | redis: 4 | host: localhost 5 | port: 6379 6 | 7 | 8 | --------------------------------------------------------------------------------