厉害了!手撸一个SpringBoot缓存系统,性能杠杠的!
点击上方蓝字关注我们
阅读本文大概需要 21 分钟。
来自:cnblogs.com/jeffwongishandsome
一、通用缓存接口
1、缓存基础算法
FIFO(First In First Out),先进先出,和OS里的FIFO思路相同,如果一个数据先进入缓存中,当缓存满的时候,应当把先进入缓存的数据给移除掉。
LFU(Least Frequently Used),不经常使用,如果一个数据在近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。
LRU(Least Recently Used),近少使用,如果一个数据在近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把久没有被访问到的数据移除。
2、接口定义
package com.power.demo.cache.contract;
import java.util.function.Function;
/**
* 缓存提供者接口
**/
public interface CacheProviderService {
/**
* 查询缓存
*
* @param key 缓存键 不可为空
**/
<T extends Object> T get(String key);
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
**/
<T extends Object> T get(String key, Function<String, T> function);
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
**/
<T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm);
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
<T extends Object> T get(String key, Function<String, T> function, Long expireTime);
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
<T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm, Long expireTime);
/**
* 设置缓存键值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
**/
<T extends Object> void set(String key, T obj);
/**
* 设置缓存键值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
<T extends Object> void set(String key, T obj, Long expireTime);
/**
* 移除缓存
*
* @param key 缓存键 不可为空
**/
void remove(String key);
/**
* 是否存在缓存
*
* @param key 缓存键 不可为空
**/
boolean contains(String key);
}
二、本地缓存
1、什么是Guava
2、添加依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
3、实现接口
package com.power.demo.cache.impl;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.Maps;
import com.power.demo.cache.contract.CacheProviderService;
import com.power.demo.common.AppConst;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
/*
* 本地缓存提供者服务 (Guava Cache)
* */
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
@Qualifier("localCacheService")
public class LocalCacheProviderImpl implements CacheProviderService {
private static Map<String, Cache<String, Object>> _cacheMap = Maps.newConcurrentMap();
static {
Cache<String, Object> cacheContainer = CacheBuilder.newBuilder()
.maximumSize(AppConst.CACHE_MAXIMUM_SIZE)
.expireAfterWrite(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS)//后一次写入后的一段时间移出
//.expireAfterAccess(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS) //后一次访问后的一段时间移出
.recordStats()//开启统计功能
.build();
_cacheMap.put(String.valueOf(AppConst.CACHE_MINUTE), cacheContainer);
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
**/
public <T extends Object> T get(String key) {
T obj = get(key, null, null, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function) {
T obj = get(key, function, key, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm) {
T obj = get(key, function, funcParm, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function, Long expireTime) {
T obj = get(key, function, key, expireTime);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm, Long expireTime) {
T obj = null;
if (StringUtils.isEmpty(key) == true) {
return obj;
}
expireTime = getExpireTime(expireTime);
Cache<String, Object> cacheContainer = getCacheContainer(expireTime);
try {
if (function == null) {
obj = (T) cacheContainer.getIfPresent(key);
} else {
final Long cachedTime = expireTime;
obj = (T) cacheContainer.get(key, () -> {
T retObj = function.apply(funcParm);
return retObj;
});
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 设置缓存键值 直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
**/
public <T extends Object> void set(String key, T obj) {
set(key, obj, AppConst.CACHE_MINUTE);
}
/**
* 设置缓存键值 直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> void set(String key, T obj, Long expireTime) {
if (StringUtils.isEmpty(key) == true) {
return;
}
if (obj == null) {
return;
}
expireTime = getExpireTime(expireTime);
Cache<String, Object> cacheContainer = getCacheContainer(expireTime);
cacheContainer.put(key, obj);
}
/**
* 移除缓存
*
* @param key 缓存键 不可为空
**/
public void remove(String key) {
if (StringUtils.isEmpty(key) == true) {
return;
}
long expireTime = getExpireTime(AppConst.CACHE_MINUTE);
Cache<String, Object> cacheContainer = getCacheContainer(expireTime);
cacheContainer.invalidate(key);
}
/**
* 是否存在缓存
*
* @param key 缓存键 不可为空
**/
public boolean contains(String key) {
boolean exists = false;
if (StringUtils.isEmpty(key) == true) {
return exists;
}
Object obj = get(key);
if (obj != null) {
exists = true;
}
return exists;
}
private static Lock lock = new ReentrantLock();
private Cache<String, Object> getCacheContainer(Long expireTime) {
Cache<String, Object> cacheContainer = null;
if (expireTime == null) {
return cacheContainer;
}
String mapKey = String.valueOf(expireTime);
if (_cacheMap.containsKey(mapKey) == true) {
cacheContainer = _cacheMap.get(mapKey);
return cacheContainer;
}
try {
lock.lock();
cacheContainer = CacheBuilder.newBuilder()
.maximumSize(AppConst.CACHE_MAXIMUM_SIZE)
.expireAfterWrite(expireTime, TimeUnit.MILLISECONDS)//后一次写入后的一段时间移出
//.expireAfterAccess(AppConst.CACHE_MINUTE, TimeUnit.MILLISECONDS) //后一次访问后的一段时间移出
.recordStats()//开启统计功能
.build();
_cacheMap.put(mapKey, cacheContainer);
} finally {
lock.unlock();
}
return cacheContainer;
}
/**
* 获取过期时间 单位:毫秒
*
* @param expireTime 传人的过期时间 单位毫秒 如小于1分钟,默认为10分钟
**/
private Long getExpireTime(Long expireTime) {
Long result = expireTime;
if (expireTime == null || expireTime < AppConst.CACHE_MINUTE / 10) {
result = AppConst.CACHE_MINUTE;
}
return result;
}
}
4、注意事项
expireAfterWrite:后一次写入后的一段时间移出。
expireAfterAccess:后一次访问后的一段时间移出。
三、分布式缓存
1、什么是Redis
Values:Strings、Hash、Lists、 Sets、 Sorted sets。考虑到Redis单线程操作模式,Value的粒度不应该过大,缓存的值越大,越容易造成阻塞和排队。
2、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3、配置Redis
## Redis缓存相关配置
#Redis数据库索引(默认为0)
spring.redis.database=0
#Redis服务器地址
spring.redis.host=127.0.0.1
#Redis服务器端口
spring.redis.port=6379
#Redis服务器密码(默认为空)
spring.redis.password=123321
#Redis连接超时时间 默认:5分钟(单位:毫秒)
spring.redis.timeout=300000ms
#Redis连接池大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=512
#Redis连接池中的小空闲连接
spring.redis.jedis.pool.min-idle=0
#Redis连接池中的大空闲连接
spring.redis.jedis.pool.max-idle=8
#Redis连接池大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
package com.power.demo.cache.config;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis缓存配置类
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
return RedisCacheManager.create(connectionFactory);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
//Jedis的Key和Value的序列化器默认值是JdkSerializationRedisSerializer
//经实验,JdkSerializationRedisSerializer通过RedisDesktopManager看到的键值对不能正常解析
//设置key的序列化器
template.setKeySerializer(new StringRedisSerializer());
////设置value的序列化器 默认值是JdkSerializationRedisSerializer
//使用Jackson序列化器的问题是,复杂对象可能序列化失败,比如JodaTime的DateTime类型
// //使用Jackson2,将对象序列化为JSON
// Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
// //json转对象类,不设置默认的会将json转成hashmap
// ObjectMapper om = new ObjectMapper();
// om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
// jackson2JsonRedisSerializer.setObjectMapper(om);
// template.setValueSerializer(jackson2JsonRedisSerializer);
//将redis连接工厂设置到模板类中
template.setConnectionFactory(factory);
return template;
}
// //自定义缓存key生成策略
// @Bean
// public KeyGenerator keyGenerator() {
// return new KeyGenerator() {
// @Override
// public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
// StringBuffer sb = new StringBuffer();
// sb.append(target.getClass().getName());
// sb.append(method.getName());
// for (Object obj : params) {
// if (obj == null) {
// continue;
// }
// sb.append(obj.toString());
// }
// return sb.toString();
// }
// };
// }
}
JdkSerializationRedisSerializer:使用Java序列化;
StringRedisSerializer:序列化String类型的key和value;
GenericToStringSerializer:使用Spring转换服务进行序列化;
JacksonJsonRedisSerializer:使用Jackson 1,将对象序列化为JSON;
Jackson2JsonRedisSerializer:使用Jackson 2,将对象序列化为JSON;
OxmSerializer:使用Spring O/X映射的编排器和解排器(marshaler和unmarshaler)实现序列化,用于XML序列化;
4、实现接口
package com.power.demo.cache.impl;
import com.power.demo.cache.contract.CacheProviderService;
import com.power.demo.common.AppConst;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
@Qualifier("redisCacheService")
public class RedisCacheProviderImpl implements CacheProviderService {
@Resource
private RedisTemplate<Serializable, Object> redisTemplate;
/**
* 查询缓存
*
* @param key 缓存键 不可为空
**/
public <T extends Object> T get(String key) {
T obj = get(key, null, null, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function) {
T obj = get(key, function, key, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm) {
T obj = get(key, function, funcParm, AppConst.CACHE_MINUTE);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function, Long expireTime) {
T obj = get(key, function, key, expireTime);
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm, Long expireTime) {
T obj = null;
if (StringUtils.isEmpty(key) == true) {
return obj;
}
expireTime = getExpireTime(expireTime);
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
obj = (T) operations.get(key);
if (function != null && obj == null) {
obj = function.apply(funcParm);
if (obj != null) {
set(key, obj, expireTime);//设置缓存信息
}
}
} catch (Exception e) {
e.printStackTrace();
}
return obj;
}
/**
* 设置缓存键值 直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
**/
public <T extends Object> void set(String key, T obj) {
set(key, obj, AppConst.CACHE_MINUTE);
}
/**
* 设置缓存键值 直接向缓存中插入值,这会直接覆盖掉给定键之前映射的值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> void set(String key, T obj, Long expireTime) {
if (StringUtils.isEmpty(key) == true) {
return;
}
if (obj == null) {
return;
}
expireTime = getExpireTime(expireTime);
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, obj);
redisTemplate.expire(key, expireTime, TimeUnit.MILLISECONDS);
}
/**
* 移除缓存
*
* @param key 缓存键 不可为空
**/
public void remove(String key) {
if (StringUtils.isEmpty(key) == true) {
return;
}
redisTemplate.delete(key);
}
/**
* 是否存在缓存
*
* @param key 缓存键 不可为空
**/
public boolean contains(String key) {
boolean exists = false;
if (StringUtils.isEmpty(key) == true) {
return exists;
}
Object obj = get(key);
if (obj != null) {
exists = true;
}
return exists;
}
/**
* 获取过期时间 单位:毫秒
*
* @param expireTime 传人的过期时间 单位毫秒 如小于1分钟,默认为10分钟
**/
private Long getExpireTime(Long expireTime) {
Long result = expireTime;
if (expireTime == null || expireTime < AppConst.CACHE_MINUTE / 10) {
result = AppConst.CACHE_MINUTE;
}
return result;
}
}
四、缓存“及时”过期问题
五、二级缓存
##是否启用本地缓存
spring.power.isuselocalcache=1
##是否启用Redis缓存
spring.power.isuserediscache=1
package com.power.demo.cache;
import com.google.common.collect.Lists;
import com.power.demo.cache.contract.CacheProviderService;
import com.power.demo.common.AppConst;
import com.power.demo.common.AppField;
import com.power.demo.util.ConfigUtil;
import com.power.demo.util.PowerLogger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
/*
* 支持多缓存提供程序多级缓存的缓存帮助类
* */
@Configuration
@ComponentScan(basePackages = AppConst.BASE_PACKAGE_NAME)
public class PowerCacheBuilder {
@Autowired
@Qualifier("localCacheService")
private CacheProviderService localCacheService;
@Autowired
@Qualifier("redisCacheService")
private CacheProviderService redisCacheService;
private static List<CacheProviderService> _listCacheProvider = Lists.newArrayList();
private static final Lock providerLock = new ReentrantLock();
/**
* 初始化缓存提供者 默认优先级:先本地缓存,后分布式缓存
**/
private List<CacheProviderService> getCacheProviders() {
if (_listCacheProvider.size() > 0) {
return _listCacheProvider;
}
//线程安全
try {
providerLock.tryLock(1000, TimeUnit.MILLISECONDS);
if (_listCacheProvider.size() > 0) {
return _listCacheProvider;
}
String isUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_LOCAL_CACHE);
CacheProviderService cacheProviderService = null;
//启用本地缓存
if ("1".equalsIgnoreCase(isUseCache)) {
_listCacheProvider.add(localCacheService);
}
isUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_REDIS_CACHE);
//启用Redis缓存
if ("1".equalsIgnoreCase(isUseCache)) {
_listCacheProvider.add(redisCacheService);
resetCacheVersion();//设置分布式缓存版本号
}
PowerLogger.info("初始化缓存提供者成功,共有" + _listCacheProvider.size() + "个");
} catch (Exception e) {
e.printStackTrace();
_listCacheProvider = Lists.newArrayList();
PowerLogger.error("初始化缓存提供者发生异常:{}", e);
} finally {
providerLock.unlock();
}
return _listCacheProvider;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
**/
public <T extends Object> T get(String key) {
T obj = null;
//key = generateVerKey(key);//构造带版本的缓存键
for (CacheProviderService provider : getCacheProviders()) {
obj = provider.get(key);
if (obj != null) {
return obj;
}
}
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function) {
T obj = null;
for (CacheProviderService provider : getCacheProviders()) {
if (obj == null) {
obj = provider.get(key, function);
} else if (function != null && obj != null) {//查询并设置其他缓存提供者程序缓存
provider.get(key, function);
}
//如果callable函数为空 而缓存对象不为空 及时跳出循环并返回
if (function == null && obj != null) {
return obj;
}
}
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm) {
T obj = null;
for (CacheProviderService provider : getCacheProviders()) {
if (obj == null) {
obj = provider.get(key, function, funcParm);
} else if (function != null && obj != null) {//查询并设置其他缓存提供者程序缓存
provider.get(key, function, funcParm);
}
//如果callable函数为空 而缓存对象不为空 及时跳出循环并返回
if (function == null && obj != null) {
return obj;
}
}
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> T get(String key, Function<String, T> function, long expireTime) {
T obj = null;
for (CacheProviderService provider : getCacheProviders()) {
if (obj == null) {
obj = provider.get(key, function, expireTime);
} else if (function != null && obj != null) {//查询并设置其他缓存提供者程序缓存
provider.get(key, function, expireTime);
}
//如果callable函数为空 而缓存对象不为空 及时跳出循环并返回
if (function == null && obj != null) {
return obj;
}
}
return obj;
}
/**
* 查询缓存
*
* @param key 缓存键 不可为空
* @param function 如没有缓存,调用该callable函数返回对象 可为空
* @param funcParm function函数的调用参数
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object, M extends Object> T get(String key, Function<M, T> function, M funcParm, long expireTime) {
T obj = null;
for (CacheProviderService provider : getCacheProviders()) {
if (obj == null) {
obj = provider.get(key, function, funcParm, expireTime);
} else if (function != null && obj != null) {//查询并设置其他缓存提供者程序缓存
provider.get(key, function, funcParm, expireTime);
}
//如果callable函数为空 而缓存对象不为空 及时跳出循环并返回
if (function == null && obj != null) {
return obj;
}
}
return obj;
}
/**
* 设置缓存键值 直接向缓存中插入或覆盖值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
**/
public <T extends Object> void set(String key, T obj) {
//key = generateVerKey(key);//构造带版本的缓存键
for (CacheProviderService provider : getCacheProviders()) {
provider.set(key, obj);
}
}
/**
* 设置缓存键值 直接向缓存中插入或覆盖值
*
* @param key 缓存键 不可为空
* @param obj 缓存值 不可为空
* @param expireTime 过期时间(单位:毫秒) 可为空
**/
public <T extends Object> void set(String key, T obj, Long expireTime) {
//key = generateVerKey(key);//构造带版本的缓存键
for (CacheProviderService provider : getCacheProviders()) {
provider.set(key, obj, expireTime);
}
}
/**
* 移除缓存
*
* @param key 缓存键 不可为空
**/
public void remove(String key) {
//key = generateVerKey(key);//构造带版本的缓存键
if (StringUtils.isEmpty(key) == true) {
return;
}
for (CacheProviderService provider : getCacheProviders()) {
provider.remove(key);
}
}
/**
* 是否存在缓存
*
* @param key 缓存键 不可为空
**/
public boolean contains(String key) {
boolean exists = false;
//key = generateVerKey(key);//构造带版本的缓存键
if (StringUtils.isEmpty(key) == true) {
return exists;
}
Object obj = get(key);
if (obj != null) {
exists = true;
}
return exists;
}
/**
* 获取分布式缓存版本号
**/
public String getCacheVersion() {
String version = "";
boolean isUseCache = checkUseRedisCache();
//未启用Redis缓存
if (isUseCache == false) {
return version;
}
version = redisCacheService.get(AppConst.CACHE_VERSION_KEY);
return version;
}
/**
* 重置分布式缓存版本 如果启用分布式缓存,设置缓存版本
**/
public String resetCacheVersion() {
String version = "";
boolean isUseCache = checkUseRedisCache();
//未启用Redis缓存
if (isUseCache == false) {
return version;
}
//设置缓存版本
version = String.valueOf(Math.abs(UUID.randomUUID().hashCode()));
redisCacheService.set(AppConst.CACHE_VERSION_KEY, version);
return version;
}
/**
* 如果启用分布式缓存,获取缓存版本,重置查询的缓存key,可以实现相对实时的缓存过期控制
* <p>
* 如没有启用分布式缓存,缓存key不做修改,直接返回
**/
public String generateVerKey(String key) {
String result = key;
if (StringUtils.isEmpty(key) == true) {
return result;
}
boolean isUseCache = checkUseRedisCache();
//没有启用分布式缓存,缓存key不做修改,直接返回
if (isUseCache == false) {
return result;
}
String version = redisCacheService.get(AppConst.CACHE_VERSION_KEY);
if (StringUtils.isEmpty(version) == true) {
return result;
}
result = String.format("%s_%s", result, version);
return result;
}
/**
* 验证是否启用分布式缓存
**/
private boolean checkUseRedisCache() {
boolean isUseCache = false;
String strIsUseCache = ConfigUtil.getConfigVal(AppField.IS_USE_REDIS_CACHE);
isUseCache = "1".equalsIgnoreCase(strIsUseCache);
return isUseCache;
}
}
@Test
public void testCacheVerson() throws Exception {
String version = cacheBuilder.getCacheVersion();
System.out.println(String.format("当前缓存版本:%s", version));
String cacheKey = cacheBuilder.generateVerKey("goods778899");
GoodsVO goodsVO = new GoodsVO();
goodsVO.setGoodsId(UUID.randomUUID().toString());
goodsVO.setCreateTime(new Date());
goodsVO.setCreateDate(new DateTime(new Date()));
goodsVO.setGoodsType(1024);
goodsVO.setGoodsCode("123456789");
goodsVO.setGoodsName("我的测试商品");
cacheBuilder.set(cacheKey, goodsVO);
GoodsVO goodsVO1 = cacheBuilder.get(cacheKey);
Assert.assertNotNull(goodsVO1);
version = cacheBuilder.resetCacheVersion();
System.out.println(String.format("重置后的缓存版本:%s", version));
cacheKey = cacheBuilder.generateVerKey("goods112233");
cacheBuilder.set(cacheKey, goodsVO);
GoodsVO goodsVO2 = cacheBuilder.get(cacheKey);
Assert.assertNotNull(goodsVO2);
Assert.assertTrue("两个缓存对象的主键相同", goodsVO1.getGoodsId().equals(goodsVO2.getGoodsId()));
}
String cacheKey = _cacheBuilder.generateVerKey("com.power.demo.apiservice.impl.getgoodsbyid." + request.getGoodsId());
GoodsVO goodsVO = _cacheBuilder.get(cacheKey, _goodsService::getGoodsByGoodsId, request.getGoodsId());
- End -
相关文章