diff --git a/docs/cached.md b/docs/cached.md
index 1c52d3bb5..d30fb78b3 100644
--- a/docs/cached.md
+++ b/docs/cached.md
@@ -12,6 +12,7 @@
|name|未定义|缓存的名称|
|key|未定义|缓存的key,支持参数动态组合,比如"key_#{id}"|
|manager|空|缓存管理器名称, 不能含有':'、'#'、'@'字符|
+|localLimit|-1|本地缓存数量上限, 小于1表示无上限。
参数值支持方式:
100: 设置数值
${env.cache.limit}: 读取系统配置项 |
|localExpire|-1|本地缓存过期时长, 0表示永不过期, -1表示不作本地缓存。
参数值支持方式:
100: 设置数值
${env.cache.expires}: 读取系统配置项 |
|remoteExpire|-1|远程缓存过期时长, 0表示永不过期, -1表示不作远程缓存。
参数值支持方式:
100: 设置数值
${env.cache.expires}: 读取系统配置项 |
|nullable|false|是否可以缓存null值|
diff --git a/src/main/java/org/redkale/cached/Cached.java b/src/main/java/org/redkale/cached/Cached.java
index 1edddf079..68ca08848 100644
--- a/src/main/java/org/redkale/cached/Cached.java
+++ b/src/main/java/org/redkale/cached/Cached.java
@@ -51,6 +51,15 @@ public @interface Cached {
*/
String manager() default "";
+ /**
+ * 本地缓存数量上限, 小于1表示无上限
+ * 参数值支持方式:
+ * 100: 设置数值 ${env.cache.limit}: 读取系统配置项
+ *
+ * @return 数量上限
+ */
+ String localLimit() default "-1";
+
/**
* 本地缓存过期时长, 0表示永不过期, -1表示不作本地缓存。
* 参数值支持方式:
diff --git a/src/main/java/org/redkale/cached/CachedManager.java b/src/main/java/org/redkale/cached/CachedManager.java
index ef97b554a..22b174917 100644
--- a/src/main/java/org/redkale/cached/CachedManager.java
+++ b/src/main/java/org/redkale/cached/CachedManager.java
@@ -123,12 +123,19 @@ public interface CachedManager extends Resourcable {
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
*/
public T localGetSet(
- String name, String key, Type type, boolean nullable, Duration expire, ThrowSupplier supplier);
+ String name,
+ String key,
+ Type type,
+ boolean nullable,
+ int localLimit,
+ Duration expire,
+ ThrowSupplier supplier);
/**
* 本地异步获取缓存数据, 过期返回null
@@ -138,6 +145,7 @@ public interface CachedManager extends Resourcable {
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
@@ -147,6 +155,7 @@ public interface CachedManager extends Resourcable {
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration expire,
ThrowSupplier> supplier);
@@ -384,6 +393,7 @@ public interface CachedManager extends Resourcable {
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param localExpire 本地过期时长,Duration.ZERO为永不过期,为null表示不本地缓存
* @param remoteExpire 远程过期时长,Duration.ZERO为永不过期,为null表示不远程缓存
* @param supplier 数据函数
@@ -394,6 +404,7 @@ public interface CachedManager extends Resourcable {
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration localExpire,
Duration remoteExpire,
ThrowSupplier supplier);
@@ -406,6 +417,7 @@ public interface CachedManager extends Resourcable {
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param localExpire 本地过期时长,Duration.ZERO为永不过期,为null表示不本地缓存
* @param remoteExpire 远程过期时长,Duration.ZERO为永不过期,为null表示不远程缓存
* @param supplier 数据函数
@@ -416,6 +428,7 @@ public interface CachedManager extends Resourcable {
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration localExpire,
Duration remoteExpire,
ThrowSupplier> supplier);
diff --git a/src/main/java/org/redkale/cached/spi/CachedAction.java b/src/main/java/org/redkale/cached/spi/CachedAction.java
index 0ae3d816b..9f305d8b7 100644
--- a/src/main/java/org/redkale/cached/spi/CachedAction.java
+++ b/src/main/java/org/redkale/cached/spi/CachedAction.java
@@ -74,6 +74,9 @@ public class CachedAction {
// 父对象
private Object service;
+ // 本地缓存数量上线,> 0才有效
+ private int localLimit;
+
// 本地缓存过期时长,Duration.ZERO为永不过期,为null表示不本地缓存
private Duration localExpire;
@@ -106,6 +109,7 @@ public class CachedAction {
MultiHashKey dynKey = MultiHashKey.create(paramNames, key);
this.keyGenerator = CachedKeyGenerator.create(dynKey);
}
+ this.localLimit = Integer.parseInt(environment.getPropertyValue(cached.getLocalLimit()));
this.localExpire = createDuration(cached.getLocalExpire());
this.remoteExpire = createDuration(cached.getRemoteExpire());
((CachedActionFunc) this.manager).addAction(this);
@@ -120,6 +124,7 @@ public class CachedAction {
keyGenerator.generate(service, this, args),
resultType,
nullable,
+ localLimit,
localExpire,
remoteExpire,
(ThrowSupplier) supplier);
@@ -129,6 +134,7 @@ public class CachedAction {
keyGenerator.generate(service, this, args),
resultType,
nullable,
+ localLimit,
localExpire,
remoteExpire,
supplier);
diff --git a/src/main/java/org/redkale/cached/spi/CachedEntry.java b/src/main/java/org/redkale/cached/spi/CachedEntry.java
index 24f2e82d7..eeab705aa 100644
--- a/src/main/java/org/redkale/cached/spi/CachedEntry.java
+++ b/src/main/java/org/redkale/cached/spi/CachedEntry.java
@@ -21,8 +21,11 @@ public class CachedEntry {
private String manager;
private String name;
+
private String key;
+ private String localLimit;
+
private String localExpire;
private String remoteExpire;
@@ -37,6 +40,7 @@ public class CachedEntry {
this.manager = cached.manager();
this.name = cached.name();
this.key = cached.key();
+ this.localLimit = cached.localLimit();
this.localExpire = cached.localExpire();
this.remoteExpire = cached.remoteExpire();
this.timeUnit = cached.timeUnit();
@@ -47,6 +51,7 @@ public class CachedEntry {
this.manager = cached.manager();
this.name = cached.name();
this.key = cached.key();
+ this.localLimit = cached.localLimit();
this.localExpire = cached.localExpire();
this.remoteExpire = cached.remoteExpire();
this.timeUnit = cached.timeUnit();
@@ -65,6 +70,10 @@ public class CachedEntry {
return key;
}
+ public String getLocalLimit() {
+ return localLimit;
+ }
+
public String getLocalExpire() {
return localExpire;
}
diff --git a/src/main/java/org/redkale/cached/spi/CachedLocalSource.java b/src/main/java/org/redkale/cached/spi/CachedLocalSource.java
index a1dabd2cd..f33662945 100644
--- a/src/main/java/org/redkale/cached/spi/CachedLocalSource.java
+++ b/src/main/java/org/redkale/cached/spi/CachedLocalSource.java
@@ -6,12 +6,15 @@ package org.redkale.cached.spi;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.BiConsumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.redkale.annotation.AutoLoad;
@@ -38,8 +41,8 @@ public class CachedLocalSource implements Service {
private final JsonConvert convert = JsonFactory.create().skipAllIgnore(true).getConvert();
- // key: name, sub-key: key
- private final ConcurrentHashMap> container = new ConcurrentHashMap<>();
+ // key: name
+ private final ConcurrentHashMap container = new ConcurrentHashMap<>();
private ScheduledThreadPoolExecutor scheduler;
@@ -83,24 +86,24 @@ public class CachedLocalSource implements Service {
}
}
- public void set(String name, String key, long millis, Type type, T value) {
+ public void set(String name, String key, int localLimit, long millis, Type type, T value) {
// millis > 0 才需要过期设置
String json = convert.convertTo(type, value);
container
- .computeIfAbsent(name, n -> new ConcurrentHashMap<>())
- .computeIfAbsent(key, k -> new CacheItem(json))
+ .computeIfAbsent(name, n -> new CacheMap(localLimit))
+ .computeIfAbsent(key, json)
.set(json, millis);
}
public T get(String name, String key, Type type) {
- Map map = container.get(name);
+ CacheMap map = container.get(name);
CacheItem item = map == null ? null : map.get(key);
String json = item == null || item.isExpired() ? null : item.getValue();
return json == null ? null : convert.convertFrom(type, json);
}
public long del(String name, String key) {
- Map map = container.get(name);
+ CacheMap map = container.get(name);
return map != null && map.remove(key) != null ? 1 : 0;
}
@@ -112,11 +115,83 @@ public class CachedLocalSource implements Service {
return CompletableFuture.completedFuture(del(name, key));
}
- public CompletableFuture setAsync(String name, String key, long millis, Type type, T value) {
- return CompletableFuture.runAsync(() -> set(name, key, millis, type, value));
+ public CompletableFuture setAsync(
+ String name, String key, int localLimit, long millis, Type type, T value) {
+ return CompletableFuture.runAsync(() -> set(name, key, localLimit, millis, type, value));
}
- protected static class CacheItem {
+ public int getKeyCount(String name) {
+ CacheMap map = container.get(name);
+ return map == null ? -1 : map.size();
+ }
+
+ protected static class CacheMap {
+
+ protected final ReentrantLock lock = new ReentrantLock();
+
+ protected final ConcurrentHashMap map = new ConcurrentHashMap<>();
+
+ protected int limit;
+
+ public CacheMap(int limit) {
+ this.limit = limit;
+ }
+
+ public void forEach(BiConsumer action) {
+ map.forEach(action);
+ }
+
+ public CacheItem remove(String key) {
+ return map.remove(key);
+ }
+
+ public CacheItem get(String key) {
+ return map.get(key);
+ }
+
+ public CacheItem computeIfAbsent(String key, String json) {
+ if (limit > 0) {
+ AtomicBoolean added = new AtomicBoolean();
+ CacheItem item = map.computeIfAbsent(key, k -> {
+ added.set(true);
+ return new CacheItem(key, json);
+ });
+ if (added.get()) {
+ checkLimit();
+ }
+ return item;
+ } else {
+ return map.computeIfAbsent(key, k -> new CacheItem(key, json));
+ }
+ }
+
+ public int size() {
+ return map.size();
+ }
+
+ void checkLimit() {
+ int l = limit;
+ if (l > 0 && map.size() > l) {
+ lock.lock();
+ try {
+ if (l > 0 && map.size() > l) {
+ List items = new ArrayList<>(map.values());
+ Collections.sort(items);
+ int count = map.size() - l;
+ for (int i = 0; i < count; i++) {
+ map.remove(items.get(i).getKey());
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+ }
+ }
+
+ protected static class CacheItem implements Comparable {
+
+ private final String key;
@Nullable // json格式
protected String value;
@@ -124,17 +199,29 @@ public class CachedLocalSource implements Service {
// 为0表示永久, 大于0表示有过期时间
private long endMillis;
- public CacheItem(String value) {
+ private long createTime = System.currentTimeMillis();
+
+ public CacheItem(String key, String value) {
+ this.key = key;
this.value = value;
}
+ public String getKey() {
+ return key;
+ }
+
public String getValue() {
return value;
}
+ public long getCreateTime() {
+ return createTime;
+ }
+
public void set(String value, long millis) {
this.value = value;
- this.endMillis = millis > 0 ? (System.currentTimeMillis() + millis) : 0;
+ this.createTime = System.currentTimeMillis();
+ this.endMillis = millis > 0 ? (this.createTime + millis) : 0;
}
@ConvertDisabled
@@ -145,5 +232,17 @@ public class CachedLocalSource implements Service {
boolean isExpired(long now) {
return endMillis > 0 && now >= endMillis;
}
+
+ @Override
+ public int compareTo(CacheItem o) {
+ long t1 = this.createTime;
+ long t2 = o == null ? 0 : o.createTime;
+ return t1 == t2 ? 0 : (t1 > t2 ? 1 : -1);
+ }
+
+ @Override
+ public String toString() {
+ return JsonConvert.root().convertTo(this);
+ }
}
}
diff --git a/src/main/java/org/redkale/cached/spi/CachedManagerService.java b/src/main/java/org/redkale/cached/spi/CachedManagerService.java
index 15ac1f374..d33ba94c4 100644
--- a/src/main/java/org/redkale/cached/spi/CachedManagerService.java
+++ b/src/main/java/org/redkale/cached/spi/CachedManagerService.java
@@ -271,20 +271,28 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
*/
@Override
public T localGetSet(
- String name, String key, Type type, boolean nullable, Duration expire, ThrowSupplier supplier) {
+ String name,
+ String key,
+ Type type,
+ boolean nullable,
+ int localLimit,
+ Duration expire,
+ ThrowSupplier supplier) {
return getSet(
- (n, k, ex, ct) -> localSource.get(name, idFor(n, k), ct),
+ (n, k, l, ex, ct) -> localSource.get(name, idFor(n, k), ct),
this::localSetCache,
name,
key,
type,
nullable,
+ localLimit,
expire,
supplier);
}
@@ -297,6 +305,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
@@ -307,15 +316,17 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration expire,
ThrowSupplier> supplier) {
return getSetAsync(
- (n, k, e, c) -> localSource.getAsync(name, idFor(n, k), c),
+ (n, k, l, e, c) -> localSource.getAsync(name, idFor(n, k), c),
this::localSetCacheAsync,
name,
key,
type,
nullable,
+ localLimit,
expire,
supplier);
}
@@ -332,7 +343,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
*/
@Override
public void localSet(String name, String key, Type type, T value, Duration expire) {
- localSetCache(name, key, type, value, expire);
+ localSetCache(name, key, 0, type, value, expire);
}
/**
@@ -396,12 +407,13 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
public T remoteGetSet(
String name, String key, Type type, boolean nullable, Duration expire, ThrowSupplier supplier) {
return getSet(
- (n, k, ex, ct) -> remoteSource.get(idFor(n, k), ct),
+ (n, k, l, ex, ct) -> remoteSource.get(idFor(n, k), ct),
this::remoteSetCache,
name,
key,
type,
nullable,
+ 0,
expire,
supplier);
}
@@ -427,12 +439,13 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
Duration expire,
ThrowSupplier> supplier) {
return getSetAsync(
- (n, k, ex, ct) -> remoteSource.getAsync(idFor(n, k), ct),
+ (n, k, l, ex, ct) -> remoteSource.getAsync(idFor(n, k), ct),
this::remoteSetCacheAsync,
name,
key,
type,
nullable,
+ 0,
expire,
supplier);
}
@@ -464,7 +477,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
*/
@Override
public CompletableFuture remoteSetAsync(String name, String key, Type type, T value, Duration expire) {
- return remoteSetCacheAsync(name, key, type, value, expire);
+ return remoteSetCacheAsync(name, key, 0, type, value, expire);
}
/**
@@ -505,7 +518,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
*/
@Override
public T bothGet(String name, String key, Type type) {
- return CachedValue.get(bothGetCache(name, key, (Duration) null, type));
+ return CachedValue.get(bothGetCache(name, key, 0, (Duration) null, type));
}
/**
@@ -519,7 +532,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
*/
@Override
public CompletableFuture bothGetAsync(String name, String key, Type type) {
- return bothGetCacheAsync(name, key, (Duration) null, type).thenApply(CachedValue::get);
+ return bothGetCacheAsync(name, key, 0, (Duration) null, type).thenApply(CachedValue::get);
}
/**
@@ -530,6 +543,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param localExpire 本地过期时长,Duration.ZERO为永不过期,为null表示不本地缓存
* @param remoteExpire 远程过期时长,Duration.ZERO为永不过期,为null表示不远程缓存
* @param supplier 数据函数
@@ -541,6 +555,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration localExpire,
Duration remoteExpire,
ThrowSupplier supplier) {
@@ -555,7 +570,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
if (remoteExpire == null) { // 只有本地缓存
Objects.requireNonNull(localExpire);
- return localGetSet(name, key, type, nullable, localExpire, supplier);
+ return localGetSet(name, key, type, nullable, localLimit, localExpire, supplier);
}
if (localExpire == null) { // 只有远程缓存
Objects.requireNonNull(remoteExpire);
@@ -563,8 +578,8 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
return getSet(
this::bothGetCache,
- (n, k, e, t, v) -> {
- localSetCache(n, k, localExpire, t, v);
+ (n, k, l, e, t, v) -> {
+ localSetCache(n, k, l, localExpire, t, v);
if (remoteSource != null) {
remoteSetCache(n, k, remoteExpire, t, v);
}
@@ -573,6 +588,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
key,
type,
nullable,
+ localLimit,
localExpire,
supplier);
}
@@ -585,6 +601,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param localExpire 本地过期时长,Duration.ZERO为永不过期,为null表示不本地缓存
* @param remoteExpire 远程过期时长,Duration.ZERO为永不过期,为null表示不远程缓存
* @param supplier 数据函数
@@ -596,6 +613,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration localExpire,
Duration remoteExpire,
ThrowSupplier> supplier) {
@@ -608,7 +626,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
if (remoteExpire == null) { // 只有本地缓存
Objects.requireNonNull(localExpire);
- return localGetSetAsync(name, key, type, nullable, localExpire, supplier);
+ return localGetSetAsync(name, key, type, nullable, localLimit, localExpire, supplier);
}
if (localExpire == null) { // 只有远程缓存
Objects.requireNonNull(remoteExpire);
@@ -616,8 +634,8 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
return getSetAsync(
this::bothGetCacheAsync,
- (n, k, e, t, v) -> {
- localSetCache(n, k, localExpire, t, v);
+ (n, k, l, e, t, v) -> {
+ localSetCache(n, k, l, localExpire, t, v);
if (remoteSource != null) {
return remoteSetCacheAsync(n, k, remoteExpire, t, v);
} else {
@@ -628,6 +646,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
key,
type,
nullable,
+ localLimit,
localExpire,
supplier);
}
@@ -647,7 +666,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
public void bothSet(String name, String key, Type type, T value, Duration localExpire, Duration remoteExpire) {
checkEnable();
if (localExpire != null) {
- localSetCache(name, key, type, value, localExpire);
+ localSetCache(name, key, 0, type, value, localExpire);
}
if (remoteExpire != null && remoteSource != null) {
remoteSetCache(name, key, type, value, remoteExpire);
@@ -674,11 +693,11 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String name, String key, Type type, T value, Duration localExpire, Duration remoteExpire) {
checkEnable();
if (localExpire != null) {
- localSetCache(name, key, type, value, localExpire);
+ localSetCache(name, key, 0, type, value, localExpire);
}
CompletableFuture future = CompletableFuture.completedFuture(null);
if (remoteSource != null && remoteExpire != null) {
- future = remoteSetCacheAsync(name, key, type, value, remoteExpire);
+ future = remoteSetCacheAsync(name, key, 0, type, value, remoteExpire);
}
if (remoteSource != null && broadcastable) {
future = future.thenCompose(r -> remoteSource
@@ -745,6 +764,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
@@ -756,6 +776,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration expire,
ThrowSupplier supplier) {
checkEnable();
@@ -764,7 +785,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
Objects.requireNonNull(supplier);
final Type cacheType = loadCacheType(type);
final String id = idFor(name, key);
- CachedValue cacheVal = getter.get(name, key, expire, cacheType);
+ CachedValue cacheVal = getter.get(name, key, localLimit, expire, cacheType);
if (CachedValue.isValid(cacheVal)) {
if (logable) {
logger.log(logLevel, "Cached got id(" + id + ") value from eitherSource");
@@ -772,7 +793,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
return cacheVal.getVal();
}
Function func = k -> {
- CachedValue oldCacheVal = getter.get(name, key, expire, cacheType);
+ CachedValue oldCacheVal = getter.get(name, key, localLimit, expire, cacheType);
if (CachedValue.isValid(oldCacheVal)) {
return oldCacheVal;
}
@@ -785,7 +806,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
throw new RedkaleException(t);
}
if (CachedValue.isValid(newCacheVal)) {
- setter.set(name, key, expire, cacheType, newCacheVal);
+ setter.set(name, key, localLimit, expire, cacheType, newCacheVal);
}
return newCacheVal;
};
@@ -807,6 +828,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param key 缓存键
* @param type 数据类型
* @param nullable 是否缓存null值
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param supplier 数据函数
* @return 数据值
@@ -818,6 +840,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
String key,
Type type,
boolean nullable,
+ int localLimit,
Duration expire,
ThrowSupplier> supplier) {
checkEnable();
@@ -825,7 +848,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
Objects.requireNonNull(supplier);
final Type cacheType = loadCacheType(type);
final String id = idFor(name, key);
- CompletableFuture> sourceFuture = getter.get(name, key, expire, cacheType);
+ CompletableFuture> sourceFuture = getter.get(name, key, localLimit, expire, cacheType);
return sourceFuture.thenCompose(val -> {
if (CachedValue.isValid(val)) {
if (logable) {
@@ -843,7 +866,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
CachedValue cacheVal = toCacheValue(nullable, v);
if (CachedValue.isValid(cacheVal)) {
- setter.set(name, key, expire, cacheType, cacheVal)
+ setter.set(name, key, localLimit, expire, cacheType, cacheVal)
.whenComplete((v2, e2) -> lock.success(CachedValue.get(cacheVal)));
} else {
lock.success(CachedValue.get(cacheVal));
@@ -857,27 +880,34 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
});
}
- protected void localSetCache(String name, String key, Type type, T value, Duration expire) {
- localSetCache(name, key, expire, loadCacheType(type, value), CachedValue.create(value));
+ protected void localSetCache(String name, String key, int localLimit, Type type, T value, Duration expire) {
+ localSetCache(name, key, localLimit, expire, loadCacheType(type, value), CachedValue.create(value));
}
protected void localSetCache(
- String name, String key, Duration expire, Type cacheType, CachedValue cacheVal) {
+ String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal) {
checkEnable();
boolean logable = logger.isLoggable(logLevel);
Objects.requireNonNull(expire);
long millis = expire.toMillis();
String id = idFor(name, key);
if (logable) {
- logger.log(logLevel, "Cached set id(" + id + ") value to localSource expire " + millis + " ms");
+ logger.log(
+ logLevel,
+ "Cached set id(" + id + ") value to localSource expire " + millis + " ms, limit " + localLimit);
}
- localSource.set(name, id, millis, cacheType, cacheVal);
+ localSource.set(name, id, localLimit, millis, cacheType, cacheVal);
}
protected void remoteSetCache(String name, String key, Type type, T value, Duration expire) {
remoteSetCache(name, key, expire, loadCacheType(type, value), CachedValue.create(value));
}
+ protected void remoteSetCache(
+ String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal) {
+ remoteSetCache(name, key, expire, cacheType, cacheVal);
+ }
+
protected void remoteSetCache(
String name, String key, Duration expire, Type cacheType, CachedValue cacheVal) {
checkEnable();
@@ -896,28 +926,35 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
protected CompletableFuture localSetCacheAsync(
- String name, String key, Type type, T value, Duration expire) {
- return localSetCacheAsync(name, key, expire, loadCacheType(type, value), CachedValue.create(value));
+ String name, String key, int localLimit, Type type, T value, Duration expire) {
+ return localSetCacheAsync(name, key, localLimit, expire, loadCacheType(type, value), CachedValue.create(value));
}
protected CompletableFuture localSetCacheAsync(
- String name, String key, Duration expire, Type cacheType, CachedValue cacheVal) {
+ String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal) {
checkEnable();
boolean logable = logger.isLoggable(logLevel);
Objects.requireNonNull(expire);
String id = idFor(name, key);
long millis = expire.toMillis();
if (logable) {
- logger.log(logLevel, "Cached set id(" + id + ") value to localSource expire " + millis + " ms");
+ logger.log(
+ logLevel,
+ "Cached set id(" + id + ") value to localSource expire " + millis + " ms, limit " + localLimit);
}
- return localSource.setAsync(name, id, millis, cacheType, cacheVal);
+ return localSource.setAsync(name, id, localLimit, millis, cacheType, cacheVal);
}
protected CompletableFuture remoteSetCacheAsync(
- String name, String key, Type type, T value, Duration expire) {
+ String name, String key, int localLimit, Type type, T value, Duration expire) {
return remoteSetCacheAsync(name, key, expire, loadCacheType(type, value), CachedValue.create(value));
}
+ protected CompletableFuture remoteSetCacheAsync(
+ String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal) {
+ return remoteSetCacheAsync(name, key, expire, cacheType, cacheVal);
+ }
+
protected CompletableFuture remoteSetCacheAsync(
String name, String key, Duration expire, Type cacheType, CachedValue cacheVal) {
checkEnable();
@@ -936,7 +973,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
}
protected CachedValue bothGetCache(
- final String name, final String key, final Duration expire, final Type cacheType) {
+ final String name, final String key, int localLimit, final Duration expire, final Type cacheType) {
checkEnable();
boolean logable = logger.isLoggable(logLevel);
String id = idFor(name, key);
@@ -954,7 +991,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
if (logable) {
logger.log(logLevel, "Cached set id(" + id + ") value to localSource from remoteSource");
}
- localSetCache(name, key, expire, cacheType, cacheVal);
+ localSetCache(name, key, localLimit, expire, cacheType, cacheVal);
}
if (logable) {
logger.log(logLevel, "Cached got id(" + id + ") value from remoteSource");
@@ -972,12 +1009,13 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
* @param 泛型
* @param name 缓存名称
* @param key 缓存键
+ * @param localLimit 本地缓存数量上限
* @param expire 过期时长,Duration.ZERO为永不过期
* @param cacheType 数据类型
* @return 数据值
*/
protected CompletableFuture> bothGetCacheAsync(
- String name, String key, Duration expire, Type cacheType) {
+ String name, String key, int localLimit, Duration expire, Type cacheType) {
checkEnable();
boolean logable = logger.isLoggable(logLevel);
String id = idFor(name, key);
@@ -996,7 +1034,7 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
if (logable) {
logger.log(logLevel, "Cached set id(" + id + ") value to localSource from remoteSource");
}
- localSetCache(name, key, expire, cacheType, v);
+ localSetCache(name, key, localLimit, expire, cacheType, v);
}
if (logable) {
logger.log(logLevel, "Cached got id(" + id + ") value from remoteSource");
@@ -1077,18 +1115,18 @@ public class CachedManagerService implements CachedManager, CachedActionFunc, Se
protected static interface GetterFunc {
- public R get(String name, String key, Duration expire, Type cacheType);
+ public R get(String name, String key, int localLimit, Duration expire, Type cacheType);
}
protected static interface SetterSyncFunc {
- public void set(String name, String key, Duration expire, Type cacheType, CachedValue cacheVal);
+ public void set(String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal);
}
protected static interface SetterAsyncFunc {
public CompletableFuture set(
- String name, String key, Duration expire, Type cacheType, CachedValue cacheVal);
+ String name, String key, int localLimit, Duration expire, Type cacheType, CachedValue cacheVal);
}
public class CacheRemoteListener implements CacheEventListener {
diff --git a/src/main/java/org/redkale/cached/spi/DynForCached.java b/src/main/java/org/redkale/cached/spi/DynForCached.java
index e15a2eded..652e77c40 100644
--- a/src/main/java/org/redkale/cached/spi/DynForCached.java
+++ b/src/main/java/org/redkale/cached/spi/DynForCached.java
@@ -30,6 +30,8 @@ public @interface DynForCached {
String key();
+ String localLimit();
+
String localExpire();
String remoteExpire();
diff --git a/src/test/java/org/redkale/test/cached/CachedManagerTest.java b/src/test/java/org/redkale/test/cached/CachedManagerTest.java
index dd52082d8..3abfe8178 100644
--- a/src/test/java/org/redkale/test/cached/CachedManagerTest.java
+++ b/src/test/java/org/redkale/test/cached/CachedManagerTest.java
@@ -53,6 +53,13 @@ public class CachedManagerTest {
bean.setRemark(bean.getRemark() + "-新备注");
Assertions.assertEquals(
manager.localGet("name", bean.getName(), CachingBean.class).toString(), json);
+
+ manager.localGetSet("group", "key1", CachingBean.class, true, 2, expire, () -> new CachingBean("v1", "r1"));
+ Utility.sleep(2);
+ manager.localGetSet("group", "key2", CachingBean.class, true, 2, expire, () -> new CachingBean("v2", "r2"));
+ Utility.sleep(2);
+ manager.localGetSet("group", "key3", CachingBean.class, true, 2, expire, () -> new CachingBean("v3", "r3"));
+ Assertions.assertEquals(2, manager.getLocalSource().getKeyCount("group"));
}
@Test
@@ -71,6 +78,7 @@ public class CachedManagerTest {
"name",
String.class,
false,
+ 0,
localExpire,
remoteExpire,
() -> bean.getName());
@@ -82,7 +90,7 @@ public class CachedManagerTest {
}
Assertions.assertEquals(1, ParallelBean.c1.get());
Utility.sleep(200);
- manager.bothGetSet("name", "name", String.class, false, localExpire, remoteExpire, () -> bean.getName());
+ manager.bothGetSet("name", "name", String.class, false, 0, localExpire, remoteExpire, () -> bean.getName());
Assertions.assertEquals(1, ParallelBean.c1.get());
Utility.sleep(300);
{
@@ -94,6 +102,7 @@ public class CachedManagerTest {
"name",
String.class,
false,
+ 0,
localExpire,
remoteExpire,
() -> bean.getName());
@@ -123,6 +132,15 @@ public class CachedManagerTest {
private String remark;
+ public CachingBean() {
+ //
+ }
+
+ public CachingBean(String name, String remark) {
+ this.name = name;
+ this.remark = remark;
+ }
+
public String getName() {
return name;
}
diff --git a/src/test/java/org/redkale/test/cached/_DynLocalCacheInstance.java b/src/test/java/org/redkale/test/cached/_DynLocalCacheInstance.java
index 676aba99f..59c76260f 100644
--- a/src/test/java/org/redkale/test/cached/_DynLocalCacheInstance.java
+++ b/src/test/java/org/redkale/test/cached/_DynLocalCacheInstance.java
@@ -50,6 +50,7 @@ public class _DynLocalCacheInstance extends CachedInstance {
key = "name",
nullable = false,
timeUnit = TimeUnit.SECONDS,
+ localLimit = "-1",
remoteExpire = "-1",
localExpire = "30")
public String getName() {
@@ -68,6 +69,7 @@ public class _DynLocalCacheInstance extends CachedInstance {
key = "#{id}_#{files.one}",
nullable = false,
timeUnit = TimeUnit.SECONDS,
+ localLimit = "-1",
remoteExpire = "60",
localExpire = "30")
public File getInfo(CachedInstance.ParamBean bean, int id, List idList, Map files) {
@@ -87,6 +89,7 @@ public class _DynLocalCacheInstance extends CachedInstance {
key = "name",
nullable = false,
timeUnit = TimeUnit.SECONDS,
+ localLimit = "-1",
remoteExpire = "-1",
localExpire = "30")
public CompletableFuture getNameAsync() {
@@ -105,6 +108,7 @@ public class _DynLocalCacheInstance extends CachedInstance {
key = "#{id}_#{files.one}",
nullable = false,
timeUnit = TimeUnit.SECONDS,
+ localLimit = "-1",
remoteExpire = "60",
localExpire = "30")
public CompletableFuture