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方式是作为整体失效的! *
	 *     1) 默认不支持缓存失效,此时使用Map方式存储数据
	 *     2) 当设置运行失效时,必须采用字符串形式的数据结构
	 *     3) 该选项只对hashmap数据结构有效。
	 * 
* @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 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 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 getList(String key, long start, long end, Class clazz){ List list = this.redisHelper.lGet(this.acquireListKey(key), start, end); if(list == null || list.size() == 0){ return null; } List 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); } } /** * 删除集合中的一个元素。 *
	 *     从左往右删除list中元素A  (1:从左往右 -1:从右往左 0:删除全部)
	 * 
* @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 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 getIterator() { throw new UnsupportedOperationException(); } @Override public Set 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 map = this.redisHelper.hmget(this.getCacheName()); return map.keySet(); } @Override public List 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 map = jedis.hgetAll(this.getCacheName()); Map map = this.redisHelper.hmget(this.getCacheName()); if(map != null){ List result = new ArrayList<>((int)size); // Cachable cacheData = null; for(Map.Entry 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根下面的某个值。注意:该方法只有特殊场景使用,
* 因为这相当于存储数据没有表名,后续很难统计数量。 * @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 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 datas = jedis.hgetAll(getCacheName()); Map datas = this.redisHelper.hmget(getCacheName()); if(datas == null){ return null; } // return datas.values(); List 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; } }