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> getInfo2Async( @@ -128,6 +132,7 @@ public class _DynLocalCacheInstance extends CachedInstance { key = "name", nullable = false, timeUnit = TimeUnit.SECONDS, + localLimit = "-1", remoteExpire = "60", localExpire = "30") public CompletableFuture getName2Async() throws IOException, InstantiationException { @@ -146,6 +151,7 @@ public class _DynLocalCacheInstance extends CachedInstance { key = "#{id}_#{files.one}", nullable = false, timeUnit = TimeUnit.SECONDS, + localLimit = "-1", remoteExpire = "60", localExpire = "30") public CompletableFuture getInfoAsync( @@ -166,6 +172,7 @@ public class _DynLocalCacheInstance extends CachedInstance { key = "name", nullable = false, timeUnit = TimeUnit.SECONDS, + localLimit = "-1", remoteExpire = "60", localExpire = "30") public String getName2() throws RedkaleException {