移除Redis依赖并改造为内存缓存
This commit is contained in:
@@ -89,30 +89,24 @@
|
||||
<artifactId>jaxb-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- redis 缓存操作 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- pool 对象池 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 解析客户端操作系统、浏览器等 -->
|
||||
<dependency>
|
||||
<groupId>nl.basjes.parse.useragent</groupId>
|
||||
<artifactId>yauaa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- servlet包 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
<!-- servlet包 -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
||||
29
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java
vendored
Normal file
29
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheEntry.java
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
class InMemoryCacheEntry
|
||||
{
|
||||
private final Object value;
|
||||
|
||||
private final Long expireAtMillis;
|
||||
|
||||
InMemoryCacheEntry(Object value, Long expireAtMillis)
|
||||
{
|
||||
this.value = value;
|
||||
this.expireAtMillis = expireAtMillis;
|
||||
}
|
||||
|
||||
Object getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
Long getExpireAtMillis()
|
||||
{
|
||||
return expireAtMillis;
|
||||
}
|
||||
|
||||
boolean isExpired(long now)
|
||||
{
|
||||
return expireAtMillis != null && expireAtMillis.longValue() <= now;
|
||||
}
|
||||
}
|
||||
65
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java
vendored
Normal file
65
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStats.java
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
public class InMemoryCacheStats
|
||||
{
|
||||
private final String cacheType;
|
||||
|
||||
private final String mode;
|
||||
|
||||
private final long keySize;
|
||||
|
||||
private final long hitCount;
|
||||
|
||||
private final long missCount;
|
||||
|
||||
private final long expiredCount;
|
||||
|
||||
private final long writeCount;
|
||||
|
||||
public InMemoryCacheStats(String cacheType, String mode, long keySize, long hitCount, long missCount,
|
||||
long expiredCount, long writeCount)
|
||||
{
|
||||
this.cacheType = cacheType;
|
||||
this.mode = mode;
|
||||
this.keySize = keySize;
|
||||
this.hitCount = hitCount;
|
||||
this.missCount = missCount;
|
||||
this.expiredCount = expiredCount;
|
||||
this.writeCount = writeCount;
|
||||
}
|
||||
|
||||
public String getCacheType()
|
||||
{
|
||||
return cacheType;
|
||||
}
|
||||
|
||||
public String getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
public long getKeySize()
|
||||
{
|
||||
return keySize;
|
||||
}
|
||||
|
||||
public long getHitCount()
|
||||
{
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
public long getMissCount()
|
||||
{
|
||||
return missCount;
|
||||
}
|
||||
|
||||
public long getExpiredCount()
|
||||
{
|
||||
return expiredCount;
|
||||
}
|
||||
|
||||
public long getWriteCount()
|
||||
{
|
||||
return writeCount;
|
||||
}
|
||||
}
|
||||
290
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java
vendored
Normal file
290
ruoyi-common/src/main/java/com/ruoyi/common/core/cache/InMemoryCacheStore.java
vendored
Normal file
@@ -0,0 +1,290 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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<String, InMemoryCacheEntry>();
|
||||
|
||||
private final AtomicLong hitCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong missCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong expiredCount = new AtomicLong();
|
||||
|
||||
private final AtomicLong writeCount = new AtomicLong();
|
||||
|
||||
public void set(String key, Object value)
|
||||
{
|
||||
putEntry(key, new InMemoryCacheEntry(value, null));
|
||||
}
|
||||
|
||||
public void set(String key, Object value, long timeout, TimeUnit unit)
|
||||
{
|
||||
Objects.requireNonNull(unit, "TimeUnit must not be null");
|
||||
long expireAtMillis = System.currentTimeMillis() + Math.max(0L, unit.toMillis(timeout));
|
||||
putEntry(key, new InMemoryCacheEntry(value, Long.valueOf(expireAtMillis)));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key)
|
||||
{
|
||||
InMemoryCacheEntry entry = readEntry(key);
|
||||
return entry == null ? null : (T) entry.getValue();
|
||||
}
|
||||
|
||||
public boolean hasKey(String key)
|
||||
{
|
||||
return readEntry(key) != null;
|
||||
}
|
||||
|
||||
public boolean delete(String key)
|
||||
{
|
||||
return entries.remove(key) != null;
|
||||
}
|
||||
|
||||
public boolean delete(Collection<String> keys)
|
||||
{
|
||||
boolean deleted = false;
|
||||
if (keys == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (String key : keys)
|
||||
{
|
||||
deleted = delete(key) || deleted;
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public Set<String> keys(String pattern)
|
||||
{
|
||||
purgeExpiredEntries();
|
||||
Set<String> matchedKeys = new TreeSet<String>();
|
||||
for (Map.Entry<String, InMemoryCacheEntry> entry : entries.entrySet())
|
||||
{
|
||||
if (matches(pattern, entry.getKey()))
|
||||
{
|
||||
matchedKeys.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
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.getValue(), Long.valueOf(expireAtMillis))) != null;
|
||||
}
|
||||
|
||||
public long getExpire(String key)
|
||||
{
|
||||
return getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public long getExpire(String key, TimeUnit unit)
|
||||
{
|
||||
Objects.requireNonNull(unit, "TimeUnit must not be null");
|
||||
InMemoryCacheEntry entry = readEntry(key);
|
||||
if (entry == null)
|
||||
{
|
||||
return -2L;
|
||||
}
|
||||
if (entry.getExpireAtMillis() == null)
|
||||
{
|
||||
return -1L;
|
||||
}
|
||||
long remainingMillis = Math.max(0L, entry.getExpireAtMillis().longValue() - System.currentTimeMillis());
|
||||
long unitMillis = Math.max(1L, unit.toMillis(1L));
|
||||
return (remainingMillis + unitMillis - 1L) / 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.getValue()) + 1L;
|
||||
Long expireAtMillis = missingOrExpired || currentEntry.getExpireAtMillis() == null
|
||||
? Long.valueOf(now + Math.max(0L, unit.toMillis(timeout)))
|
||||
: currentEntry.getExpireAtMillis();
|
||||
if (missingOrExpired && currentEntry != null)
|
||||
{
|
||||
expiredCount.incrementAndGet();
|
||||
}
|
||||
result.set(nextValue);
|
||||
return new InMemoryCacheEntry(Long.valueOf(nextValue), expireAtMillis);
|
||||
});
|
||||
writeCount.incrementAndGet();
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
public InMemoryCacheStats snapshot()
|
||||
{
|
||||
purgeExpiredEntries();
|
||||
return new InMemoryCacheStats("IN_MEMORY", "single-instance", entries.size(), hitCount.get(), missCount.get(),
|
||||
expiredCount.get(), writeCount.get());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Map<String, T> getMap(String key)
|
||||
{
|
||||
Map<String, T> value = get(key);
|
||||
return value == null ? null : new HashMap<String, T>(value);
|
||||
}
|
||||
|
||||
public <T> void putMap(String key, Map<String, T> dataMap)
|
||||
{
|
||||
set(key, new HashMap<String, T>(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<String, T>();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Set<T> getSet(String key)
|
||||
{
|
||||
Set<T> value = get(key);
|
||||
return value == null ? null : new HashSet<T>(value);
|
||||
}
|
||||
|
||||
public <T> void putSet(String key, Set<T> dataSet)
|
||||
{
|
||||
set(key, new HashSet<T>(dataSet));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> List<T> getList(String key)
|
||||
{
|
||||
List<T> value = get(key);
|
||||
return value == null ? null : new ArrayList<T>(value);
|
||||
}
|
||||
|
||||
public <T> void putList(String key, List<T> dataList)
|
||||
{
|
||||
set(key, new ArrayList<T>(dataList));
|
||||
}
|
||||
|
||||
private void putEntry(String key, InMemoryCacheEntry entry)
|
||||
{
|
||||
entries.put(key, entry);
|
||||
writeCount.incrementAndGet();
|
||||
}
|
||||
|
||||
private InMemoryCacheEntry readEntry(String key)
|
||||
{
|
||||
InMemoryCacheEntry entry = entries.get(key);
|
||||
if (entry == null)
|
||||
{
|
||||
missCount.incrementAndGet();
|
||||
return null;
|
||||
}
|
||||
if (entry.isExpired(System.currentTimeMillis()))
|
||||
{
|
||||
removeExpiredEntry(key, entry);
|
||||
missCount.incrementAndGet();
|
||||
return null;
|
||||
}
|
||||
hitCount.incrementAndGet();
|
||||
return entry;
|
||||
}
|
||||
|
||||
private void purgeExpiredEntries()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
for (Map.Entry<String, InMemoryCacheEntry> entry : entries.entrySet())
|
||||
{
|
||||
if (entry.getValue().isExpired(now))
|
||||
{
|
||||
removeExpiredEntry(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void removeExpiredEntry(String key, InMemoryCacheEntry expectedEntry)
|
||||
{
|
||||
if (entries.remove(key, expectedEntry))
|
||||
{
|
||||
expiredCount.incrementAndGet();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean matches(String pattern, String key)
|
||||
{
|
||||
if ("*".equals(pattern))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (pattern.endsWith("*"))
|
||||
{
|
||||
return key.startsWith(pattern.substring(0, pattern.length() - 1));
|
||||
}
|
||||
return key.equals(pattern);
|
||||
}
|
||||
|
||||
private long toLong(Object value)
|
||||
{
|
||||
if (value instanceof Number)
|
||||
{
|
||||
return ((Number) value).longValue();
|
||||
}
|
||||
return Long.parseLong(String.valueOf(value));
|
||||
}
|
||||
|
||||
private void setWithOptionalTtl(String key, Object value, long ttlMillis)
|
||||
{
|
||||
if (ttlMillis > 0L)
|
||||
{
|
||||
set(key, value, ttlMillis, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
else
|
||||
{
|
||||
set(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,42 @@
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
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;
|
||||
|
||||
/**
|
||||
* spring redis 工具类
|
||||
*
|
||||
* @author ruoyi
|
||||
**/
|
||||
@SuppressWarnings(value = { "unchecked", "rawtypes" })
|
||||
@Component
|
||||
public class RedisCache
|
||||
{
|
||||
@Autowired
|
||||
public RedisTemplate redisTemplate;
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStats;
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 本地缓存门面,保留原有 RedisCache 业务入口。
|
||||
*
|
||||
* @author ruoyi
|
||||
**/
|
||||
@SuppressWarnings(value = { "unchecked", "rawtypes" })
|
||||
@Component
|
||||
public class RedisCache
|
||||
{
|
||||
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);
|
||||
}
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value)
|
||||
{
|
||||
cacheStore.set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
@@ -44,10 +46,10 @@ public class RedisCache
|
||||
* @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);
|
||||
}
|
||||
public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
|
||||
{
|
||||
cacheStore.set(key, value, timeout.longValue(), timeUnit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置有效时间
|
||||
@@ -69,10 +71,10 @@ public class RedisCache
|
||||
* @param unit 时间单位
|
||||
* @return true=设置成功;false=设置失败
|
||||
*/
|
||||
public boolean expire(final String key, final long timeout, final TimeUnit unit)
|
||||
{
|
||||
return redisTemplate.expire(key, timeout, unit);
|
||||
}
|
||||
public boolean expire(final String key, final long timeout, final TimeUnit unit)
|
||||
{
|
||||
return cacheStore.expire(key, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取有效时间
|
||||
@@ -80,10 +82,10 @@ public class RedisCache
|
||||
* @param key Redis键
|
||||
* @return 有效时间
|
||||
*/
|
||||
public long getExpire(final String key)
|
||||
{
|
||||
return redisTemplate.getExpire(key);
|
||||
}
|
||||
public long getExpire(final String key)
|
||||
{
|
||||
return cacheStore.getExpire(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 key是否存在
|
||||
@@ -91,10 +93,10 @@ public class RedisCache
|
||||
* @param key 键
|
||||
* @return true 存在 false不存在
|
||||
*/
|
||||
public Boolean hasKey(String key)
|
||||
{
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
public Boolean hasKey(String key)
|
||||
{
|
||||
return cacheStore.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象。
|
||||
@@ -102,21 +104,20 @@ public class RedisCache
|
||||
* @param key 缓存键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> T getCacheObject(final String key)
|
||||
{
|
||||
ValueOperations<String, T> operation = redisTemplate.opsForValue();
|
||||
return operation.get(key);
|
||||
}
|
||||
public <T> T getCacheObject(final String key)
|
||||
{
|
||||
return cacheStore.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除单个对象
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public boolean deleteObject(final String key)
|
||||
{
|
||||
return redisTemplate.delete(key);
|
||||
}
|
||||
public boolean deleteObject(final String key)
|
||||
{
|
||||
return cacheStore.delete(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除集合对象
|
||||
@@ -124,10 +125,19 @@ public class RedisCache
|
||||
* @param collection 多个对象
|
||||
* @return
|
||||
*/
|
||||
public boolean deleteObject(final Collection collection)
|
||||
{
|
||||
return redisTemplate.delete(collection) > 0;
|
||||
}
|
||||
public boolean deleteObject(final Collection collection)
|
||||
{
|
||||
if (collection == null || collection.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
List<String> keys = new ArrayList<String>(collection.size());
|
||||
for (Object item : collection)
|
||||
{
|
||||
keys.add(String.valueOf(item));
|
||||
}
|
||||
return cacheStore.delete(keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存List数据
|
||||
@@ -136,11 +146,11 @@ public class RedisCache
|
||||
* @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;
|
||||
}
|
||||
public <T> long setCacheList(final String key, final List<T> dataList)
|
||||
{
|
||||
cacheStore.putList(key, dataList);
|
||||
return dataList == null ? 0 : dataList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的list对象
|
||||
@@ -148,10 +158,10 @@ public class RedisCache
|
||||
* @param key 缓存的键值
|
||||
* @return 缓存键值对应的数据
|
||||
*/
|
||||
public <T> List<T> getCacheList(final String key)
|
||||
{
|
||||
return redisTemplate.opsForList().range(key, 0, -1);
|
||||
}
|
||||
public <T> List<T> getCacheList(final String key)
|
||||
{
|
||||
return cacheStore.getList(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Set
|
||||
@@ -160,16 +170,11 @@ public class RedisCache
|
||||
* @param dataSet 缓存的数据
|
||||
* @return 缓存数据的对象
|
||||
*/
|
||||
public <T> BoundSetOperations<String, T> 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;
|
||||
}
|
||||
public <T> long setCacheSet(final String key, final Set<T> dataSet)
|
||||
{
|
||||
cacheStore.putSet(key, dataSet);
|
||||
return dataSet == null ? 0 : dataSet.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的set
|
||||
@@ -177,10 +182,10 @@ public class RedisCache
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Set<T> getCacheSet(final String key)
|
||||
{
|
||||
return redisTemplate.opsForSet().members(key);
|
||||
}
|
||||
public <T> Set<T> getCacheSet(final String key)
|
||||
{
|
||||
return cacheStore.getSet(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存Map
|
||||
@@ -188,12 +193,13 @@ public class RedisCache
|
||||
* @param key
|
||||
* @param dataMap
|
||||
*/
|
||||
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
|
||||
{
|
||||
if (dataMap != null) {
|
||||
redisTemplate.opsForHash().putAll(key, dataMap);
|
||||
}
|
||||
}
|
||||
public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
|
||||
{
|
||||
if (dataMap != null)
|
||||
{
|
||||
cacheStore.putMap(key, dataMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的Map
|
||||
@@ -201,10 +207,10 @@ public class RedisCache
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public <T> Map<String, T> getCacheMap(final String key)
|
||||
{
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
public <T> Map<String, T> getCacheMap(final String key)
|
||||
{
|
||||
return cacheStore.getMap(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 往Hash中存入数据
|
||||
@@ -213,10 +219,10 @@ public class RedisCache
|
||||
* @param hKey Hash键
|
||||
* @param value 值
|
||||
*/
|
||||
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
|
||||
{
|
||||
redisTemplate.opsForHash().put(key, hKey, value);
|
||||
}
|
||||
public <T> void setCacheMapValue(final String key, final String hKey, final T value)
|
||||
{
|
||||
cacheStore.putMapValue(key, hKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Hash中的数据
|
||||
@@ -225,11 +231,11 @@ public class RedisCache
|
||||
* @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);
|
||||
}
|
||||
public <T> T getCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
Map<String, T> map = cacheStore.getMap(key);
|
||||
return map == null ? null : map.get(hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个Hash中的数据
|
||||
@@ -238,10 +244,20 @@ public class RedisCache
|
||||
* @param hKeys Hash键集合
|
||||
* @return Hash对象集合
|
||||
*/
|
||||
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
|
||||
{
|
||||
return redisTemplate.opsForHash().multiGet(key, hKeys);
|
||||
}
|
||||
public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
|
||||
{
|
||||
Map<String, T> map = cacheStore.getMap(key);
|
||||
if (map == null || hKeys == null || hKeys.isEmpty())
|
||||
{
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<T> values = new ArrayList<T>(hKeys.size());
|
||||
for (Object hKey : hKeys)
|
||||
{
|
||||
values.add(map.get(String.valueOf(hKey)));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除Hash中的某条数据
|
||||
@@ -250,10 +266,10 @@ public class RedisCache
|
||||
* @param hKey Hash键
|
||||
* @return 是否成功
|
||||
*/
|
||||
public boolean deleteCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
return redisTemplate.opsForHash().delete(key, hKey) > 0;
|
||||
}
|
||||
public boolean deleteCacheMapValue(final String key, final String hKey)
|
||||
{
|
||||
return cacheStore.deleteMapValue(key, hKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得缓存的基本对象列表
|
||||
@@ -261,8 +277,23 @@ public class RedisCache
|
||||
* @param pattern 字符串前缀
|
||||
* @return 对象列表
|
||||
*/
|
||||
public Collection<String> keys(final String pattern)
|
||||
{
|
||||
return redisTemplate.keys(pattern);
|
||||
}
|
||||
}
|
||||
public Collection<String> keys(final String 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();
|
||||
}
|
||||
}
|
||||
|
||||
58
ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java
vendored
Normal file
58
ruoyi-common/src/test/java/com/ruoyi/common/core/cache/InMemoryCacheStoreTest.java
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.ruoyi.common.core.cache;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class InMemoryCacheStoreTest
|
||||
{
|
||||
@Test
|
||||
void shouldExpireEntryAfterTtl() throws Exception
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("captcha_codes:1", "1234", 20L, TimeUnit.MILLISECONDS);
|
||||
Thread.sleep(40L);
|
||||
assertNull(store.get("captcha_codes:1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldReturnPrefixKeysInSortedOrder()
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("login_tokens:a", "A");
|
||||
store.set("login_tokens:b", "B");
|
||||
store.set("sys_dict:x", "X");
|
||||
|
||||
Set<String> expected = new HashSet<String>();
|
||||
expected.add("login_tokens:a");
|
||||
expected.add("login_tokens:b");
|
||||
assertEquals(expected, store.keys("login_tokens:*"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldTrackHitsMissesWritesAndExpiredEntries() throws Exception
|
||||
{
|
||||
InMemoryCacheStore store = new InMemoryCacheStore();
|
||||
store.set("captcha_codes:2", "5678", 20L, TimeUnit.MILLISECONDS);
|
||||
assertTrue(store.hasKey("captcha_codes:2"));
|
||||
assertEquals("5678", store.get("captcha_codes:2"));
|
||||
Thread.sleep(40L);
|
||||
assertNull(store.get("captcha_codes:2"));
|
||||
assertFalse(store.hasKey("captcha_codes:2"));
|
||||
|
||||
InMemoryCacheStats stats = store.snapshot();
|
||||
assertEquals("IN_MEMORY", stats.getCacheType());
|
||||
assertEquals("single-instance", stats.getMode());
|
||||
assertEquals(0, stats.getKeySize());
|
||||
assertEquals(2L, stats.getHitCount());
|
||||
assertEquals(2L, stats.getMissCount());
|
||||
assertEquals(1L, stats.getExpiredCount());
|
||||
assertEquals(1L, stats.getWriteCount());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import com.ruoyi.common.core.cache.InMemoryCacheStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RedisCacheTest
|
||||
{
|
||||
@Test
|
||||
void shouldSupportSetGetDeleteAndExpireThroughRedisCacheFacade()
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
cache.setCacheObject("login_tokens:abc", "payload", 1, TimeUnit.SECONDS);
|
||||
assertEquals("payload", cache.getCacheObject("login_tokens:abc"));
|
||||
assertTrue(cache.hasKey("login_tokens:abc"));
|
||||
assertTrue(cache.deleteObject("login_tokens:abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldSupportKeysBulkDeleteAndRemainingTtl()
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
cache.setCacheObject("login_tokens:a", "A", 5, TimeUnit.SECONDS);
|
||||
cache.setCacheObject("login_tokens:b", "B", 5, TimeUnit.SECONDS);
|
||||
cache.setCacheObject("sys_dict:x", "X");
|
||||
|
||||
assertNotNull(cache.getExpire("login_tokens:a"));
|
||||
assertTrue(cache.getExpire("login_tokens:a") > 0L);
|
||||
assertEquals(2, cache.keys("login_tokens:*").size());
|
||||
|
||||
Collection<String> keys = new ArrayList<String>();
|
||||
keys.add("login_tokens:a");
|
||||
keys.add("login_tokens:b");
|
||||
assertTrue(cache.deleteObject(keys));
|
||||
assertFalse(cache.hasKey("login_tokens:a"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIncrementCounterWithinTtlWindow() throws Exception
|
||||
{
|
||||
RedisCache cache = new RedisCache(new InMemoryCacheStore());
|
||||
assertEquals(1L, cache.increment("rate_limit:test", 50L, TimeUnit.MILLISECONDS));
|
||||
assertEquals(2L, cache.increment("rate_limit:test", 50L, TimeUnit.MILLISECONDS));
|
||||
Thread.sleep(70L);
|
||||
assertNull(cache.getCacheObject("rate_limit:test"));
|
||||
assertEquals(1L, cache.increment("rate_limit:test", 50L, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user