package com.walker.support.redis.cache;
|
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
import com.walker.cache.Cachable;
|
import com.walker.cache.Cache;
|
import com.walker.cache.CacheConfig;
|
import com.walker.cache.CacheOperateListener;
|
import com.walker.cache.util.KeyUtils;
|
import com.walker.infrastructure.ApplicationRuntimeException;
|
import com.walker.infrastructure.core.ApplicationBeanDestroied;
|
import com.walker.infrastructure.core.ApplicationBeanInitialized;
|
import com.walker.infrastructure.utils.JsonUtils;
|
import com.walker.infrastructure.utils.StringUtils;
|
import com.walker.support.redis.RedisHelper;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.Iterator;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
|
public class RedisCache implements Cache, ApplicationBeanInitialized, ApplicationBeanDestroied {
|
|
/**
|
*
|
*/
|
private static final long serialVersionUID = -2870330465438805899L;
|
|
protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
|
|
private String cacheName;
|
CacheOperateListener cacheOperateListener = null;
|
boolean hasCacheListener = false;
|
|
private RedisHelper redisHelper;
|
|
// 2023-06-13 集合的名字,用来存储集合数据。
|
private String nameKeySet = null;
|
|
public boolean isSupportExpiredCache() {
|
return supportExpiredCache;
|
}
|
|
/**
|
* 设置是否支持缓存失效,因为redis失效是针对key,因此HashMap方式是作为整体失效的!
|
* <pre>
|
* 1) 默认不支持缓存失效,此时使用Map方式存储数据
|
* 2) 当设置运行失效时,必须采用字符串形式的数据结构
|
* 3) 该选项只对hashmap数据结构有效。
|
* </pre>
|
* @param supportExpiredCache
|
* @date 2024-01-05
|
*/
|
public void setSupportExpiredCache(boolean supportExpiredCache) {
|
this.supportExpiredCache = supportExpiredCache;
|
}
|
|
// 2024-01-05 是否支持缓存失效,因为redis失效是针对key,因此HashMap方式是作为整体失效的!
|
private boolean supportExpiredCache = false;
|
|
public RedisHelper getRedisHelper() {
|
return redisHelper;
|
}
|
|
public void setRedisHelper(RedisHelper redisHelper) {
|
this.redisHelper = redisHelper;
|
}
|
|
public RedisCache(String providerName, Map<String, String> param){
|
this.setCacheName(providerName);
|
this.nameKeySet = this.getCacheName() + ":set";
|
}
|
|
@Override
|
public void shutdown() {
|
}
|
|
@Override
|
public void startup() {}
|
|
private void checkSupportExpired(){
|
if(!this.supportExpiredCache){
|
throw new IllegalStateException("该缓存'" + this.getCacheName() + "'不支持缓存失效,请调用不带时间参数方法!如果需要失效请调用方法:setSupportExpiredCache(true)");
|
}
|
}
|
|
private void checkNotSupportExpired(){
|
if(this.supportExpiredCache){
|
throw new IllegalStateException("该缓存支持时间失效(supportExpiredCache = true),请调用带时间参数的方法:put(String key, Object data, long expiredSeconds)");
|
}
|
}
|
|
@Override
|
public void put(String key, Object data) {
|
this.checkNotSupportExpired();
|
try{
|
if(data instanceof String){
|
this.redisHelper.hset(this.getCacheName(), key, data.toString());
|
} else {
|
this.redisHelper.hset(this.getCacheName(), key, JsonUtils.objectToJsonString(data));
|
}
|
if(hasCacheListener){
|
cacheOperateListener.onPut(data);
|
}
|
} catch(Exception e){
|
if(e instanceof JsonMappingException){
|
logger.error("JsonMappingException: " + key + ", data=" + data.toString(), e);
|
} else if(e instanceof JsonProcessingException) {
|
logger.error("JsonProcessingException: " + key + ", data=" + data.toString(), e);
|
} else {
|
e.printStackTrace();
|
}
|
}
|
}
|
|
@Override
|
public void put(String key, Object data, long expiredSeconds){
|
this.checkSupportExpired();
|
try{
|
if(data instanceof String){
|
// this.redisHelper.hset(this.getCacheName(), key, data.toString(), expiredSeconds);
|
this.redisHelper.set(this.acquireListKey(key), data.toString(), expiredSeconds);
|
} else {
|
// this.redisHelper.hset(this.getCacheName(), key, JsonUtils.objectToJsonString(data), expiredSeconds);
|
this.redisHelper.set(this.acquireListKey(key), JsonUtils.objectToJsonString(data), expiredSeconds);
|
}
|
if(hasCacheListener){
|
cacheOperateListener.onPut(data);
|
}
|
} catch (Exception e){
|
if(e instanceof JsonMappingException){
|
logger.error("JsonMappingException: " + key + ", data=" + data.toString(), e);
|
} else if(e instanceof JsonProcessingException) {
|
logger.error("JsonProcessingException: " + key + ", data=" + data.toString(), e);
|
} else {
|
e.printStackTrace();
|
}
|
}
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~ 2023-06-13 操作集合,但目前没有使用,感觉删除整个集合还比较麻烦。
|
//~ 2023-06-14 确定使用该对象,并测试结果。
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
private String acquireListKey(String key){
|
return KeyUtils.acquireListKey(this.getCacheName(), key);
|
}
|
|
@Override
|
public void putList(String key, List<Object> list, long expiredSeconds){
|
if(expiredSeconds > 0){
|
this.checkSupportExpired();
|
}
|
Object tmp = null;
|
try{
|
for(Object data: list){
|
tmp = data;
|
if(data instanceof String){
|
// jsonList.add(data.toString());
|
this.redisHelper.lSet(this.acquireListKey(key), data.toString(), expiredSeconds);
|
} else {
|
// jsonList.add(JsonUtils.objectToJsonString(data));
|
this.redisHelper.lSet(this.acquireListKey(key), JsonUtils.objectToJsonString(data), expiredSeconds);
|
}
|
}
|
tmp = JsonUtils.objectToJsonString(list);
|
} catch (Exception e) {
|
throw new ApplicationRuntimeException("redis缓存操作失败,key = "+key + ", data="+tmp, e);
|
}
|
|
// if(expiredSeconds > 0){
|
//// this.redisHelper.lSetList(this.acquireListKey(key), jsonList, expiredSeconds);
|
// this.redisHelper.lSet(this.acquireListKey(key), tmp, expiredSeconds);
|
// } else {
|
//// this.redisHelper.lSetList(this.acquireListKey(key), jsonList);
|
// this.redisHelper.lSet(this.acquireListKey(key), tmp);
|
// }
|
}
|
|
@Override
|
public void putListAppend(String key, Object data){
|
if(data instanceof String){
|
this.redisHelper.lSet(this.acquireListKey(key), data.toString());
|
} else {
|
try {
|
this.redisHelper.lSet(this.acquireListKey(key), JsonUtils.objectToJsonString(data));
|
} catch (Exception e) {
|
throw new RuntimeException("redis缓存操作失败,key = "+key + "putListAppend = "+data, e);
|
}
|
}
|
}
|
|
/**
|
* 获取整个集合值,0 到 -1代表所有值
|
* @param key
|
* @param start
|
* @param end
|
* @return
|
*/
|
@Override
|
public List<Object> getList(String key, long start, long end, Class<?> clazz){
|
List<Object> list = this.redisHelper.lGet(this.acquireListKey(key), start, end);
|
if(list == null || list.size() == 0){
|
return null;
|
}
|
List<Object> resultList = new ArrayList<>();
|
try{
|
Object javaObj = null;
|
for(Object obj : list){
|
javaObj = JsonUtils.jsonStringToObject(obj.toString(), clazz);
|
resultList.add(javaObj);
|
}
|
return resultList;
|
} catch (Exception ex){
|
throw new RuntimeException("getList获取缓存集合错误:" + ex.getMessage(), ex);
|
}
|
}
|
|
/**
|
* 删除集合中的一个元素。
|
* <pre>
|
* 从左往右删除list中元素A (1:从左往右 -1:从右往左 0:删除全部)
|
* </pre>
|
* @param key 集合唯一key
|
* @param value 元素内容,对象字符串
|
* @date 2023-06-13
|
*/
|
@Override
|
public void removeList(String key, Object value){
|
String data = null;
|
if(value instanceof String){
|
data = value.toString();
|
} else {
|
try {
|
data = JsonUtils.objectToJsonString(value);
|
} catch (Exception e) {
|
throw new RuntimeException("removeList错误:" + e.getMessage() + ", value = " + value, e);
|
}
|
}
|
this.redisHelper.lRemove(this.acquireListKey(key), 0, data);
|
}
|
|
/**
|
* 删除一个集合,待测试验证。
|
* @param key
|
* @date 2023-08-03
|
*/
|
@Override
|
public void removeList(String key) {
|
this.redisHelper.removeList(this.acquireListKey(key));
|
}
|
|
/**
|
* 获取集合元素数量。
|
* @param key
|
* @return
|
*/
|
@Override
|
public long getListSize(String key){
|
return this.redisHelper.lGetListSize(this.acquireListKey(key));
|
}
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@Override
|
public Object remove(String key) {
|
try{
|
if(this.supportExpiredCache){
|
// 支持过期的数据,使用的是普通字符串(非hashmap),所以应调用删除key。2024-01-05
|
this.redisHelper.del(this.acquireListKey(key));
|
} else {
|
this.redisHelper.hdel(this.getCacheName(), key);
|
}
|
} catch(Exception e){
|
logger.error("缓存 remove 错误:" + key, e);
|
}
|
|
// if(hasCacheListener){
|
// cacheOperateListener.onRemove(v);
|
// }
|
return null;
|
}
|
|
@Override
|
public void remove(List<String> keys) {
|
if(keys != null){
|
try{
|
if(this.supportExpiredCache){
|
// 支持过期的数据,使用的是普通字符串(非hashmap),所以应调用删除key。2024-01-05
|
this.redisHelper.del(StringUtils.toStringArray(keys));
|
} else {
|
this.redisHelper.hdel(this.getCacheName(), keys);
|
}
|
} catch(Exception e){
|
logger.error("缓存批量 remove 错误:" + keys, e);
|
}
|
}
|
}
|
|
/**
|
* 返回持久化的的缓存数据数量,如:redis持久的
|
* @return
|
*/
|
@Override
|
public long getPersistentSize(){
|
try{
|
if(this.supportExpiredCache){
|
// 普通key,通过空间命名设置,目前无法直接通过api获取数量。2024-01-05
|
// 例如(cache.user.online:123456)
|
return 0;
|
} else {
|
return this.redisHelper.hSize(this.getCacheName());
|
}
|
} catch(Exception e){
|
logger.error("getPersistentSize异常:" + e.getMessage(), e);
|
}
|
return 0;
|
}
|
|
@Override
|
public Object get(String key) {
|
try{
|
if(this.supportExpiredCache){
|
// 支持过期的数据,使用的是普通字符串(非hashmap),所以应调用get:key。2024-01-05
|
return this.redisHelper.get(this.acquireListKey(key));
|
} else {
|
return this.redisHelper.hget(this.getCacheName(), key);
|
}
|
} catch(Exception e){
|
logger.error("缓存获得数据异常 get:" + key, e);
|
}
|
return null;
|
}
|
|
/**
|
* 给定对象类型,返回缓存的对象。该方法在redis实现中用到
|
* @param key
|
* @param clazz
|
* @return
|
*/
|
@Override
|
// @Deprecated
|
public Object get(String key, Class<?> clazz){
|
try{
|
Object json = null;
|
if(this.supportExpiredCache){
|
// 支持过期的数据,使用的是普通字符串(非hashmap),所以应调用get:key。2024-01-05
|
json = this.redisHelper.get(this.acquireListKey(key));
|
} else {
|
json = this.redisHelper.hget(this.getCacheName(), key);
|
}
|
if(json == null || StringUtils.isEmpty(json.toString())){
|
return null;
|
}
|
if(clazz == String.class){
|
return json.toString();
|
}
|
return JsonUtils.jsonStringToObject(json.toString(), clazz);
|
|
} catch(Exception e){
|
logger.error("缓存获得数据异常 get:" + key, e);
|
}
|
|
return null;
|
}
|
|
@Override
|
public void replace(String key, Object data) {
|
this.put(key, data);
|
}
|
|
@Override
|
public void clear() {
|
logger.warn("清除redis缓存:" + this.getCacheName());
|
try{
|
this.redisHelper.hdel(this.getCacheName());
|
} catch(Exception e){
|
logger.error("清空缓存表异常 clear:" + this.getCacheName(), e);
|
}
|
}
|
|
@Override
|
public void updateCacheDataTimeStamp(String key) {
|
throw new UnsupportedOperationException();
|
}
|
|
@Override
|
public String getCacheName() {
|
return cacheName;
|
}
|
|
@Override
|
public void setCacheName(String name) {
|
this.cacheName = name;
|
}
|
|
@Override
|
public boolean isWriteOnDiskAfterShutdown() {
|
return false;
|
}
|
|
@Override
|
public void setWriteOnDiskAfterShutdown(boolean boo) {
|
|
}
|
|
@Override
|
public long getExpiredTime() {
|
return 0;
|
}
|
|
@Override
|
public void setExpiredTime(int seconds) {
|
|
}
|
|
@Override
|
public Iterator<Cachable> getIterator() {
|
throw new UnsupportedOperationException();
|
}
|
|
@Override
|
public Set<Object> getKeys(){
|
if(this.supportExpiredCache){
|
throw new IllegalStateException("该缓存支持失效时间,无法获取缓存key集合。cacheName=" + this.getCacheName());
|
}
|
long size = this.redisHelper.hSize(this.getCacheName());
|
if(size >= CacheConfig.INIT_CACHEMAP_SIZE){
|
throw new RuntimeException("缓存数量过大,无法调用遍历方法:" + this.getCacheName());
|
}
|
Map<Object, Object> map = this.redisHelper.hmget(this.getCacheName());
|
return map.keySet();
|
}
|
|
@Override
|
public List<String> getIterator(Class<?> clazz) {
|
if(this.supportExpiredCache){
|
throw new IllegalStateException("该缓存支持失效时间,无法获取缓存集合数据。cacheName=" + this.getCacheName());
|
}
|
try{
|
long size = this.redisHelper.hSize(this.getCacheName());
|
if(size >= CacheConfig.INIT_CACHEMAP_SIZE){
|
throw new RuntimeException("缓存数量过大,无法调用遍历方法:" + this.getCacheName());
|
}
|
// Map<String, String> map = jedis.hgetAll(this.getCacheName());
|
Map<Object, Object> map = this.redisHelper.hmget(this.getCacheName());
|
if(map != null){
|
List<String> result = new ArrayList<>((int)size);
|
// Cachable cacheData = null;
|
for(Map.Entry<Object, Object> entry : map.entrySet()){
|
// cacheData = new CacheData();
|
// cacheData.setKey(entry.getKey());
|
// cacheData.setValue(JSON.parseObject(entry.getValue(), clazz));
|
result.add(entry.getValue().toString());
|
}
|
return result;
|
}
|
} catch(Exception e){
|
logger.error("getIterator:" + this.getCacheName(), e);
|
}
|
return null;
|
}
|
|
@Override
|
public void setCacheOperateListener(CacheOperateListener cacheOperateListener) {
|
|
}
|
|
/**
|
* 返回redis根下面的某个值。注意:该方法只有特殊场景使用,<br>
|
* 因为这相当于存储数据没有表名,后续很难统计数量。
|
* @param key
|
* @return
|
*/
|
@Deprecated
|
public String getRootData(String key){
|
try{
|
Object obj = this.redisHelper.get(key);
|
if(obj != null){
|
return obj.toString();
|
}
|
} catch(Exception e){
|
logger.error("getRootData:" + key, e);
|
}
|
return null;
|
}
|
|
@Override
|
public long size(){
|
// Jedis jedis = null;
|
try{
|
// jedis = this.getRedis();
|
// Long result = jedis.hlen(getCacheName());
|
// if(result == null){
|
// return 0;
|
// }
|
// return result.longValue();
|
if(this.supportExpiredCache){
|
logger.warn("支持失效时间的缓存数据,无法获取元素个数,cacheName=" + this.getCacheName());
|
return 0;
|
} else {
|
return this.redisHelper.hSize(this.getCacheName());
|
}
|
} catch(Exception e){
|
logger.error("size:" + e.getMessage(), e);
|
return 0;
|
}
|
}
|
|
/**
|
* 返回给定限制的缓存数据集合
|
* @param maxSize 限制的最大数量
|
* @return
|
*/
|
@Override
|
public Collection<Object> queryListLimit(int maxSize){
|
if(this.supportExpiredCache){
|
throw new IllegalStateException("该缓存支持失效时间,无法获取缓存集合数据。");
|
}
|
if(maxSize < 0 || maxSize >=Integer.MAX_VALUE){
|
return null;
|
}
|
|
// Jedis jedis = null;
|
try{
|
// jedis = this.getRedis();
|
long cacheSize = this.redisHelper.hSize(getCacheName());
|
// Long result = jedis.hlen(getCacheName());
|
// if(result != null){
|
// cacheSize = result.longValue();
|
// }
|
|
if(cacheSize <= maxSize){
|
// 当缓存数量小于给定限制,显示全部
|
// Map<String, String> datas = jedis.hgetAll(getCacheName());
|
Map<Object, Object> datas = this.redisHelper.hmget(getCacheName());
|
if(datas == null){
|
return null;
|
}
|
// return datas.values();
|
List<Object> resultList = new ArrayList<>();
|
for(Object obj : datas.values()){
|
resultList.add(obj.toString());
|
}
|
return resultList;
|
} else {
|
//
|
throw new IllegalArgumentException("缓存数量大于给定的限制值,不能显示数据,数量过大");
|
}
|
|
} catch(Exception e){
|
logger.error("queryListLimit:" + e.getMessage(), e);
|
}
|
return null;
|
}
|
}
|