完成后端移除Redis改造

This commit is contained in:
wkc
2026-03-28 10:59:34 +08:00
parent bc2582246b
commit 65fe3d4605
17 changed files with 640 additions and 391 deletions

View File

@@ -1,11 +1,18 @@
package com.ruoyi.common.core.cache;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.springframework.stereotype.Component;
@Component
public class InMemoryCacheStore
{
private final ConcurrentHashMap<String, InMemoryCacheEntry> entries = new ConcurrentHashMap<>();
@@ -21,10 +28,11 @@ public class InMemoryCacheStore
public void set(String key, Object value, long timeout, TimeUnit unit)
{
long expireAtMillis = System.currentTimeMillis() + unit.toMillis(timeout);
long expireAtMillis = System.currentTimeMillis() + Math.max(0L, unit.toMillis(timeout));
putEntry(key, new InMemoryCacheEntry(value, expireAtMillis));
}
@SuppressWarnings("unchecked")
public <T> T get(String key)
{
InMemoryCacheEntry entry = readEntry(key);
@@ -41,6 +49,16 @@ public class InMemoryCacheStore
return entries.remove(key) != null;
}
public boolean delete(Collection<String> keys)
{
boolean deleted = false;
for (String key : keys)
{
deleted = delete(key) || deleted;
}
return deleted;
}
public Set<String> keys(String pattern)
{
purgeExpiredEntries();
@@ -54,6 +72,63 @@ public class InMemoryCacheStore
return matchedKeys;
}
public boolean expire(String key, long timeout, TimeUnit unit)
{
Objects.requireNonNull(unit, "TimeUnit must not be null");
long expireAtMillis = System.currentTimeMillis() + Math.max(0L, unit.toMillis(timeout));
return entries.computeIfPresent(key, (cacheKey, entry) -> entry.isExpired(System.currentTimeMillis())
? null
: new InMemoryCacheEntry(entry.value(), expireAtMillis)) != null;
}
public long getExpire(String key)
{
return getExpire(key, TimeUnit.SECONDS);
}
public long getExpire(String key, TimeUnit unit)
{
InMemoryCacheEntry entry = readEntry(key);
if (entry == null)
{
return -2L;
}
if (entry.expireAtMillis() == null)
{
return -1L;
}
long remainingMillis = Math.max(0L, entry.expireAtMillis() - System.currentTimeMillis());
long unitMillis = Math.max(1L, unit.toMillis(1));
return (remainingMillis + unitMillis - 1) / unitMillis;
}
public long increment(String key, long timeout, TimeUnit unit)
{
Objects.requireNonNull(unit, "TimeUnit must not be null");
AtomicLong result = new AtomicLong();
entries.compute(key, (cacheKey, currentEntry) -> {
long now = System.currentTimeMillis();
boolean missingOrExpired = currentEntry == null || currentEntry.isExpired(now);
long nextValue = missingOrExpired ? 1L : toLong(currentEntry.value()) + 1L;
Long expireAtMillis = missingOrExpired || currentEntry.expireAtMillis() == null
? now + Math.max(0L, unit.toMillis(timeout))
: currentEntry.expireAtMillis();
if (missingOrExpired && currentEntry != null)
{
expiredCount.incrementAndGet();
}
result.set(nextValue);
return new InMemoryCacheEntry(nextValue, expireAtMillis);
});
writeCount.incrementAndGet();
return result.get();
}
public void clear()
{
entries.clear();
}
public InMemoryCacheStats snapshot()
{
purgeExpiredEntries();
@@ -67,6 +142,64 @@ public class InMemoryCacheStore
writeCount.get());
}
public <T> Map<String, T> getMap(String key)
{
Map<String, T> value = get(key);
return value == null ? null : new HashMap<>(value);
}
public <T> void putMap(String key, Map<String, T> dataMap)
{
set(key, new HashMap<>(dataMap));
}
public <T> void putMapValue(String key, String mapKey, T value)
{
long ttl = getExpire(key, TimeUnit.MILLISECONDS);
Map<String, T> current = getMap(key);
if (current == null)
{
current = new HashMap<>();
}
current.put(mapKey, value);
setWithOptionalTtl(key, current, ttl);
}
public boolean deleteMapValue(String key, String mapKey)
{
long ttl = getExpire(key, TimeUnit.MILLISECONDS);
Map<String, Object> current = getMap(key);
if (current == null || !current.containsKey(mapKey))
{
return false;
}
current.remove(mapKey);
setWithOptionalTtl(key, current, ttl);
return true;
}
public <T> Set<T> getSet(String key)
{
Set<T> value = get(key);
return value == null ? null : new HashSet<>(value);
}
public <T> void putSet(String key, Set<T> dataSet)
{
set(key, new HashSet<>(dataSet));
}
public <T> java.util.List<T> getList(String key)
{
java.util.List<T> value = get(key);
return value == null ? null : new java.util.ArrayList<>(value);
}
public <T> void putList(String key, java.util.List<T> dataList)
{
set(key, new java.util.ArrayList<>(dataList));
}
private void putEntry(String key, InMemoryCacheEntry entry)
{
entries.put(key, entry);
@@ -122,4 +255,23 @@ public class InMemoryCacheStore
}
return key.equals(pattern);
}
private long toLong(Object value)
{
if (value instanceof Number number)
{
return number.longValue();
}
return Long.parseLong(String.valueOf(value));
}
private void setWithOptionalTtl(String key, Object value, long ttlMillis)
{
if (ttlMillis > 0)
{
set(key, value, ttlMillis, TimeUnit.MILLISECONDS);
return;
}
set(key, value);
}
}

View File

@@ -1,268 +1,169 @@
package com.ruoyi.common.core.redis;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import com.ruoyi.common.core.cache.InMemoryCacheStats;
import com.ruoyi.common.core.cache.InMemoryCacheStore;
/**
* spring redis 工具类
* 本地缓存门面,保留原有 RedisCache 业务入口。
*
* @author ruoyi
**/
@SuppressWarnings(value = { "unchecked", "rawtypes" })
@SuppressWarnings("unchecked")
@Component
public class RedisCache
{
@Autowired
public RedisTemplate redisTemplate;
private final InMemoryCacheStore cacheStore;
public RedisCache(InMemoryCacheStore cacheStore)
{
this.cacheStore = cacheStore;
}
/**
* 缓存基本的对象Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
*/
public <T> void setCacheObject(final String key, final T value)
{
redisTemplate.opsForValue().set(key, value);
cacheStore.set(key, value);
}
/**
* 缓存基本的对象Integer、String、实体类等
*
* @param key 缓存的键值
* @param value 缓存的值
* @param timeout 时间
* @param timeUnit 时间颗粒度
*/
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
{
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
cacheStore.set(key, value, timeout.longValue(), timeUnit);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @return true=设置成功false=设置失败
*/
public boolean expire(final String key, final long timeout)
{
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
* 设置有效时间
*
* @param key Redis键
* @param timeout 超时时间
* @param unit 时间单位
* @return true=设置成功false=设置失败
*/
public boolean expire(final String key, final long timeout, final TimeUnit unit)
{
return redisTemplate.expire(key, timeout, unit);
return cacheStore.expire(key, timeout, unit);
}
/**
* 获取有效时间
*
* @param key Redis键
* @return 有效时间
*/
public long getExpire(final String key)
{
return redisTemplate.getExpire(key);
return cacheStore.getExpire(key);
}
/**
* 判断 key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key)
{
return redisTemplate.hasKey(key);
return cacheStore.hasKey(key);
}
/**
* 获得缓存的基本对象。
*
* @param key 缓存键值
* @return 缓存键值对应的数据
*/
public <T> T getCacheObject(final String key)
{
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
return cacheStore.get(key);
}
/**
* 删除单个对象
*
* @param key
*/
public boolean deleteObject(final String key)
{
return redisTemplate.delete(key);
return cacheStore.delete(key);
}
/**
* 删除集合对象
*
* @param collection 多个对象
* @return
*/
public boolean deleteObject(final Collection collection)
{
return redisTemplate.delete(collection) > 0;
if (collection == null || collection.isEmpty())
{
return false;
}
List<String> keys = new ArrayList<>(collection.size());
for (Object item : collection)
{
keys.add(String.valueOf(item));
}
return cacheStore.delete(keys);
}
/**
* 缓存List数据
*
* @param key 缓存的键值
* @param dataList 待缓存的List数据
* @return 缓存的对象
*/
public <T> long setCacheList(final String key, final List<T> dataList)
{
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
cacheStore.putList(key, dataList);
return dataList == null ? 0 : dataList.size();
}
/**
* 获得缓存的list对象
*
* @param key 缓存的键值
* @return 缓存键值对应的数据
*/
public <T> List<T> getCacheList(final String key)
{
return redisTemplate.opsForList().range(key, 0, -1);
return cacheStore.getList(key);
}
/**
* 缓存Set
*
* @param key 缓存键值
* @param dataSet 缓存的数据
* @return 缓存数据的对象
*/
public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
public <T> long setCacheSet(final String key, final Set<T> dataSet)
{
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext())
{
setOperation.add(it.next());
}
return setOperation;
cacheStore.putSet(key, dataSet);
return dataSet == null ? 0 : dataSet.size();
}
/**
* 获得缓存的set
*
* @param key
* @return
*/
public <T> Set<T> getCacheSet(final String key)
{
return redisTemplate.opsForSet().members(key);
return cacheStore.getSet(key);
}
/**
* 缓存Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
{
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
if (dataMap != null)
{
cacheStore.putMap(key, dataMap);
}
}
/**
* 获得缓存的Map
*
* @param key
* @return
*/
public <T> Map<String, T> getCacheMap(final String key)
{
return redisTemplate.opsForHash().entries(key);
return cacheStore.getMap(key);
}
/**
* 往Hash中存入数据
*
* @param key Redis键
* @param hKey Hash键
* @param value 值
*/
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
{
redisTemplate.opsForHash().put(key, hKey, value);
cacheStore.putMapValue(key, hKey, value);
}
/**
* 获取Hash中的数据
*
* @param key Redis键
* @param hKey Hash键
* @return Hash中的对象
*/
public <T> T getCacheMapValue(final String key, final String hKey)
{
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
Map<String, T> map = cacheStore.getMap(key);
return map == null ? null : map.get(hKey);
}
/**
* 获取多个Hash中的数据
*
* @param key Redis键
* @param hKeys Hash键集合
* @return Hash对象集合
*/
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
{
return redisTemplate.opsForHash().multiGet(key, hKeys);
Map<String, T> map = cacheStore.getMap(key);
if (map == null || hKeys == null || hKeys.isEmpty())
{
return Collections.emptyList();
}
List<T> values = new ArrayList<>(hKeys.size());
for (Object hKey : hKeys)
{
values.add(map.get(String.valueOf(hKey)));
}
return values;
}
/**
* 删除Hash中的某条数据
*
* @param key Redis键
* @param hKey Hash键
* @return 是否成功
*/
public boolean deleteCacheMapValue(final String key, final String hKey)
{
return redisTemplate.opsForHash().delete(key, hKey) > 0;
return cacheStore.deleteMapValue(key, hKey);
}
/**
* 获得缓存的基本对象列表
*
* @param pattern 字符串前缀
* @return 对象列表
*/
public Collection<String> keys(final String pattern)
{
return redisTemplate.keys(pattern);
return cacheStore.keys(pattern);
}
public long increment(String key, long timeout, TimeUnit unit)
{
return cacheStore.increment(key, timeout, unit);
}
public InMemoryCacheStats getCacheStats()
{
return cacheStore.snapshot();
}
public void clear()
{
cacheStore.clear();
}
}

View File

@@ -4,7 +4,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.core.domain.entity.SysDictData;
import com.ruoyi.common.core.redis.RedisCache;
@@ -41,10 +41,14 @@ public class DictUtils
*/
public static List<SysDictData> getDictCache(String key)
{
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(arrayCache))
Object cacheObject = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (cacheObject instanceof List<?> listCache)
{
return arrayCache.toList(SysDictData.class);
return (List<SysDictData>) listCache;
}
if (StringUtils.isNotNull(cacheObject))
{
return JSON.parseArray(JSON.toJSONString(cacheObject), SysDictData.class);
}
return null;
}