CacheManager

This commit is contained in:
redkale
2023-12-11 10:55:03 +08:00
parent d589eef853
commit 8e51f99c7a
12 changed files with 1148 additions and 43 deletions

View File

@@ -45,8 +45,9 @@
【节点全局唯一】 @since 2.8.0
全局Serivce的缓存设置没配置该节点将自动创建一个。
enabled 是否开启缓存功能。默认: true
source: 远程CacheSource的资源名
-->
<caching enabled="true"/>
<caching enabled="true" source="xxx"/>
<!--
【节点全局唯一】

View File

@@ -17,6 +17,7 @@ module org.redkale {
exports org.redkale.asm;
exports org.redkale.boot;
exports org.redkale.boot.watch;
exports org.redkale.caching;
exports org.redkale.cluster;
exports org.redkale.convert;
exports org.redkale.convert.bson;

View File

@@ -15,7 +15,7 @@ import java.util.concurrent.TimeUnit;
* //TODO 待实现
*
* 标记在Service的缓存接口, 方法有以下限制: <br>
* 1、方法返回类型不能是void
* 1、方法返回类型不能是void/CompletableFuture&#60;Void&#62;
* 2、方法必须是protected/public
* 3、方法不能是final
*

View File

@@ -14,10 +14,15 @@ import java.lang.annotation.Target;
* //TODO 待实现
*
* 标记在Service的锁接口, 方法有以下限制: <br>
* 1、方法返回类型不能是void
* 1、方法返回类型不能是void/CompletableFuture&#60;Void&#62;
* 2、方法必须是protected/public
* 3、方法不能是final
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.8.0
*/
@Inherited

View File

@@ -14,6 +14,7 @@ import java.util.concurrent.TimeUnit;
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.8.0
*/
@Target({ElementType.METHOD})

View File

@@ -0,0 +1,35 @@
/*
*
*/
package org.redkale.caching;
import org.redkale.convert.json.JsonConvert;
/**
*
* 缓存配置
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.8.0
*/
public class CacheConfig {
private boolean enabled = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -1,40 +0,0 @@
/*
*
*/
package org.redkale.caching;
import java.lang.reflect.Type;
import org.redkale.source.CacheMemorySource;
import org.redkale.source.CacheSource;
/**
* //TODO 待实现
*
* @author zhangjx
*/
public class CacheFactory {
protected CacheSource localSource = new CacheMemorySource("caching");
protected CacheSource remoteSource;
protected CacheFactory(CacheSource remoteSource) {
this.remoteSource = remoteSource;
}
public static CacheFactory create(CacheSource remoteSource) {
return new CacheFactory(remoteSource);
}
protected long hdelLocal(String map, String key) {
return localSource.hdel(map, key);
}
protected <T> void hsetLocal(final String map, final String key, final Type type, final T value) {
localSource.hset(map, key, type, value);
}
protected <T> T hgetLocal(final String map, final String key, final Type type) {
return localSource.hget(map, key, type);
}
}

View File

@@ -0,0 +1,359 @@
/*
*
*/
package org.redkale.caching;
import java.lang.reflect.Type;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import org.redkale.annotation.Nonnull;
import org.redkale.annotation.Nullable;
import org.redkale.source.CacheMemorySource;
import org.redkale.source.CacheSource;
import org.redkale.util.TypeToken;
/**
* //TODO 待实现
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
*
* @since 2.8.0
*/
public class CacheManager {
//缓存配置项
protected final CacheConfig config;
//数据类型与CacheValue泛型的对应关系
private final ConcurrentHashMap<Type, Type> cacheValueTypes = new ConcurrentHashMap<>();
//本地缓存Source
protected final CacheSource localSource = new CacheMemorySource("caching");
//远程缓存Source
protected CacheSource remoteSource;
protected CacheManager(@Nonnull CacheConfig config, @Nullable CacheSource remoteSource) {
this.config = Objects.requireNonNull(config);
this.remoteSource = remoteSource;
}
public static CacheManager create(@Nonnull CacheConfig config, @Nullable CacheSource remoteSource) {
return new CacheManager(config, remoteSource);
}
//-------------------------------------- 本地缓存 --------------------------------------
/**
* 本地获取缓存数据, 过期返回null
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
*
* @return 数据值
*/
public <T> T localGet(final String map, final String key, final Type type) {
return get(localSource, map, key, type);
}
/**
* 本地获取字符串缓存数据, 过期返回null
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 数据值
*/
public final String localGetString(final String map, final String key) {
return get(localSource, map, key, String.class);
}
/**
* 本地缓存数据
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public <T> void localSet(final String map, final String key, final Type type, final T value, Duration expire) {
set(localSource, map, key, type, value, expire);
}
/**
* 本地缓存字符串数据
*
* @param map 缓存hash
* @param key 缓存键
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public void localSetString(final String map, final String key, final String value, Duration expire) {
set(localSource, map, key, String.class, value, expire);
}
/**
* 本地删除缓存数据
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 删除数量
*/
public long localDel(String map, String key) {
return del(localSource, map, key);
}
//-------------------------------------- 远程缓存 --------------------------------------
/**
* 远程获取缓存数据, 过期返回null
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
*
* @return 数据值
*/
public <T> T remoteGet(final String map, final String key, final Type type) {
return get(remoteSource, map, key, type);
}
/**
* 远程异步获取缓存数据, 过期返回null
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
*
* @return 数据值
*/
public <T> CompletableFuture<T> remoteGetAsync(final String map, final String key, final Type type) {
return getAsync(remoteSource, map, key, type);
}
/**
* 远程获取字符串缓存数据, 过期返回null
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 数据值
*/
public final String remoteGetString(final String map, final String key) {
return get(remoteSource, map, key, String.class);
}
/**
* 远程异步获取字符串缓存数据, 过期返回null
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 数据值
*/
public final CompletableFuture<String> remoteGetStringAsync(final String map, final String key) {
return getAsync(remoteSource, map, key, String.class);
}
/**
* 远程缓存数据
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public <T> void remoteSet(final String map, final String key, final Type type, final T value, Duration expire) {
set(remoteSource, map, key, type, value, expire);
}
/**
* 远程异步缓存数据
*
* @param <T> 泛型
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public <T> CompletableFuture<Void> remoteSetAsync(final String map, final String key, final Type type, final T value, Duration expire) {
return setAsync(remoteSource, map, key, type, value, expire);
}
/**
* 远程缓存字符串数据
*
* @param map 缓存hash
* @param key 缓存键
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public void remoteSetString(final String map, final String key, final String value, Duration expire) {
set(remoteSource, map, key, String.class, value, expire);
}
/**
* 远程异步缓存字符串数据
*
* @param map 缓存hash
* @param key 缓存键
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
public CompletableFuture<Void> remoteSetStringAsync(final String map, final String key, final String value, Duration expire) {
return setAsync(remoteSource, map, key, String.class, value, expire);
}
/**
* 远程删除缓存数据
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 删除数量
*/
public long remoteDel(String map, String key) {
return del(remoteSource, map, key);
}
/**
* 远程异步删除缓存数据
*
* @param map 缓存hash
* @param key 缓存键
*
* @return 删除数量
*/
public CompletableFuture<Long> remoteDelAsync(String map, String key) {
return delAsync(remoteSource, map, key);
}
//-------------------------------------- 内部方法 --------------------------------------
/**
* 获取缓存数据, 过期返回null
*
* @param <T> 泛型
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
*
* @return 数据值
*/
protected <T> T get(final CacheSource source, final String map, final String key, final Type type) {
CacheValue<T> val = source.hget(map, key, loadCacheType(type));
return val != null && !val.isExpired() ? val.getValue() : null;
}
/**
* 获取缓存数据, 过期返回null
*
* @param <T> 泛型
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
*
* @return 数据值
*/
protected <T> CompletableFuture<T> getAsync(final CacheSource source, final String map, final String key, final Type type) {
return source.hgetAsync(map, key, loadCacheType(type)).thenApply(v -> {
CacheValue<T> val = (CacheValue) v;
return val != null && !val.isExpired() ? (T) val.getValue() : null;
});
}
/**
* 缓存数据
*
* @param <T> 泛型
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
protected <T> void set(final CacheSource source, final String map, final String key, final Type type, final T value, Duration expire) {
Type t = loadCacheType(type, value);
source.hset(map, key, t, CacheValue.create(value, expire));
}
/**
* 缓存数据
*
* @param <T> 泛型
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
* @param type 数据类型
* @param value 数据值
* @param expire 过期时长为null表示永不过期
*/
protected <T> CompletableFuture<Void> setAsync(final CacheSource source, final String map, final String key, final Type type, final T value, Duration expire) {
Type t = loadCacheType(type, value);
return source.hsetAsync(map, key, t, CacheValue.create(value, expire));
}
/**
* 删除缓存数据
*
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
*
* @return 删除数量
*/
protected long del(final CacheSource source, String map, String key) {
return source.hdel(map, key);
}
/**
* 删除缓存数据
*
* @param source 缓存源
* @param map 缓存hash
* @param key 缓存键
*
* @return 删除数量
*/
protected CompletableFuture<Long> delAsync(final CacheSource source, String map, String key) {
return source.hdelAsync(map, key);
}
/**
* 创建数据类型创建对应CacheValue泛型
*
* @param type 数据类型为null则取value的类型
* @param value 数据值
*
* @return CacheValue泛型
*/
protected Type loadCacheType(Type type, final Object value) {
return loadCacheType(type == null ? value.getClass() : type);
}
/**
* 创建数据类型创建对应CacheValue泛型
*
* @param type 数据类型
*
* @return CacheValue泛型
*/
protected Type loadCacheType(Type type) {
return cacheValueTypes.computeIfAbsent(type, t -> TypeToken.createParameterizedType(null, CacheValue.class, type));
}
}

View File

@@ -0,0 +1,68 @@
/*
*
*/
package org.redkale.caching;
import java.time.Duration;
import org.redkale.convert.ConvertColumn;
import org.redkale.convert.ConvertDisabled;
import org.redkale.convert.json.JsonConvert;
/**
*
* 缓存对象
*
* <p>
* 详情见: https://redkale.org
*
* @author zhangjx
* @param <T> 泛型
*
* @since 2.8.0
*/
public class CacheValue<T> {
@ConvertColumn(index = 1)
private T value;
//为0表示不过期
@ConvertColumn(index = 2)
private long time;
public CacheValue() {
}
protected CacheValue(T value, Duration expire) {
this.value = value;
this.time = expire == null ? 0 : (System.currentTimeMillis() + expire.toMillis());
}
public static <T> CacheValue<T> create(T value, Duration expire) {
return new CacheValue(value, expire);
}
@ConvertDisabled
public boolean isExpired() {
return time > 0 && System.currentTimeMillis() > time;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
public String toString() {
return JsonConvert.root().convertTo(this);
}
}

View File

@@ -47,6 +47,7 @@ public class RedkaleClassLoader extends URLClassLoader {
"org.redkale.asm",
"org.redkale.boot",
"org.redkale.boot.watch",
"org.redkale.caching",
"org.redkale.cluster",
"org.redkale.convert",
"org.redkale.convert.bson",

View File

@@ -0,0 +1,32 @@
/*
*
*/
package org.redkale.test.caching;
import java.time.Duration;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.redkale.caching.CacheConfig;
import org.redkale.caching.CacheManager;
import org.redkale.source.CacheMemorySource;
import org.redkale.util.Utility;
/**
*
* @author zhangjx
*/
public class CachingTest {
public static void main(String[] args) throws Throwable {
CachingTest test = new CachingTest();
test.run();
}
@Test
public void run() throws Exception {
CacheManager cache = CacheManager.create(new CacheConfig(), new CacheMemorySource("remote"));
cache.localSetString("user", "name:haha", "haha", Duration.ofMillis(500));
Utility.sleep(501);
Assertions.assertTrue(cache.localGetString("user", "name:haha") == null);
}
}

View File

@@ -0,0 +1,642 @@
/*
*
*/
package org.redkale.test.source;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.redkale.convert.json.JsonConvert;
import org.redkale.source.CacheEventListener;
import org.redkale.source.CacheMemorySource;
import org.redkale.source.CacheScoredValue;
import org.redkale.source.Flipper;
import org.redkale.util.TypeToken;
import org.redkale.util.Utility;
/**
*
* @author zhangjx
*/
public class CacheMemorySourceTest {
public static void main(String[] args) throws Throwable {
CacheMemorySourceTest test = new CacheMemorySourceTest();
test.run();
}
@Test
public void run() throws Exception {
CacheMemorySource source = new CacheMemorySource("");
source.init(null);
System.out.println("------------------------------------");
source.del("stritem1", "stritem2", "stritem1x");
source.setString("stritem1", "value1");
source.setString("stritem2", "value2");
Assertions.assertEquals("value2", source.getDelString("stritem2"));
Assertions.assertTrue(source.getDelString("stritem2") == null);
source.setString("stritem2", "value2");
List<String> list = source.keysStartsWith("stritem");
System.out.println("stritem开头的key有两个: " + list);
Assertions.assertTrue(Utility.equalsElement(List.of("stritem2", "stritem1"), list));
Map<String, String> map = source.mgetsString("stritem1", "stritem2");
System.out.println("[有值] MGET : " + map);
Assertions.assertTrue(Utility.equalsElement(Utility.ofMap("stritem1", "value1", "stritem2", "value2"), map));
List<String> array = source.mgetString("stritem1", "stritem2");
System.out.println("[有值] MGET : " + array);
Assertions.assertTrue(Utility.equalsElement(List.of("value1", "value2"), array));
Assertions.assertFalse(source.persist("stritem1"));
Assertions.assertTrue(source.rename("stritem1", "stritem1x"));
Assertions.assertEquals("value1", source.getString("stritem1x"));
Assertions.assertEquals(null, source.getString("stritem1"));
Assertions.assertFalse(source.renamenx("stritem1x", "stritem2"));
Assertions.assertEquals("value2", source.getString("stritem2"));
Assertions.assertTrue(source.renamenx("stritem1x", "stritem1"));
source.del("intitem1", "intitem2");
source.setLong("intitem1", 333);
source.setLong("intitem2", 444);
map = source.mgetsString("intitem1", "intitem22", "intitem2");
System.out.println("[有值] MGET : " + map);
Assertions.assertTrue(Utility.equalsElement(Utility.ofMap("intitem1", "333", "intitem2", "444"), map));
array = source.mgetString("intitem1", "intitem22", "intitem2");
System.out.println("[有值] MGET : " + array);
List<String> ss = new ArrayList<>();
ss.add("333");
ss.add(null);
ss.add("444");
Assertions.assertTrue(Utility.equalsElement(ss, array));
source.del("objitem1", "objitem2");
source.mset(Utility.ofMap("objitem1", new Flipper(10), "objitem2", new Flipper(20)));
Map<String, Flipper> flippermap = source.mgets(Flipper.class, "objitem1", "objitem2");
System.out.println("[有值] MGET : " + flippermap);
Assertions.assertTrue(Utility.equalsElement(Utility.ofMap("objitem1", new Flipper(10), "objitem2", new Flipper(20)), flippermap));
source.del("key1", "key2", "300");
source.setex("key1", 1000, String.class, "value1");
source.set("key1", String.class, "value1");
source.setString("keystr1", "strvalue1");
source.setLong("keylong1", 333L);
source.set("300", String.class, "4000");
Object obj = source.getex("key1", 3500, String.class);
System.out.println("[有值] key1 GET : " + obj);
Assertions.assertEquals("value1", obj);
obj = source.get("300", String.class);
System.out.println("[有值] 300 GET : " + obj);
Assertions.assertEquals("4000", obj);
obj = source.get("key1", String.class);
System.out.println("[有值] key1 GET : " + obj);
Assertions.assertEquals("value1", obj);
obj = source.getSet("key1", String.class, "value11");
System.out.println("[旧值] key1 GETSET : " + obj);
Assertions.assertEquals("value1", obj);
obj = source.get("key2", String.class);
System.out.println("[无值] key2 GET : " + obj);
Assertions.assertNull(obj);
obj = source.getString("keystr1");
System.out.println("[有值] keystr1 GET : " + obj);
Assertions.assertEquals("strvalue1", obj);
long num = source.getLong("keylong1", 0L);
System.out.println("[有值] keylong1 GET : " + null);
Assertions.assertEquals(333L, num);
boolean bool = source.exists("key1");
System.out.println("[有值] key1 EXISTS : " + bool);
Assertions.assertTrue(bool);
bool = source.exists("key2");
System.out.println("[无值] key2 EXISTS : " + bool);
Assertions.assertFalse(bool);
source.del("keys3");
source.rpush("keys3", String.class, "vals1");
source.rpush("keys3", String.class, "vals2");
System.out.println("-------- keys3 追加了两个值 --------");
Collection col = source.lrangeString("keys3");
System.out.println("[两值] keys3 VALUES : " + col);
Assertions.assertTrue(Utility.equalsElement(List.of("vals1", "vals2"), col));
bool = source.exists("keys3");
System.out.println("[有值] keys3 EXISTS : " + bool);
Assertions.assertTrue(bool);
source.lrem("keys3", String.class, "vals1");
col = source.lrangeString("keys3");
System.out.println("[一值] keys3 VALUES : " + col);
Assertions.assertIterableEquals(List.of("vals2"), col);
source.rpush("keys3", String.class, "vals2");
source.rpush("keys3", String.class, "vals3");
source.rpush("keys3", String.class, "vals4");
Assertions.assertEquals("vals4", source.rpopString("keys3"));
source.lpush("keys3", String.class, "vals0");
Assertions.assertEquals("vals0", source.lpopString("keys3"));
String rlv = source.rpoplpush("keys3", "keys3-2", String.class);
Assertions.assertEquals("vals3", rlv);
source.del("keys3");
source.lpushString("keys3", "vals20");
source.lpushString("keys3", "vals10");
System.out.println("keys3-list: " + source.lrangeString("keys3"));
Assertions.assertEquals("vals10", source.lindexString("keys3", 0));
Assertions.assertEquals("vals20", source.lindexString("keys3", -1));
source.linsertBeforeString("keys3", "vals10", "vals00");
source.linsertAfterString("keys3", "vals10", "vals15");
Assertions.assertEquals(0, source.linsertBeforeString("keys_3", "vals10", "vals00"));
Assertions.assertEquals(-1, source.linsertBeforeString("keys3", "vals90", "vals00"));
Assertions.assertEquals(4, source.llen("keys3"));
Assertions.assertIterableEquals(List.of("vals00", "vals10", "vals15", "vals20"), source.lrangeString("keys3"));
source.del("stringmap");
source.sadd("stringmap", JsonConvert.TYPE_MAP_STRING_STRING, Utility.ofMap("a", "aa", "b", "bb"));
source.sadd("stringmap", JsonConvert.TYPE_MAP_STRING_STRING, Utility.ofMap("c", "cc", "d", "dd"));
col = source.smembers("stringmap", JsonConvert.TYPE_MAP_STRING_STRING);
System.out.println("[两值] stringmap VALUES : " + col);
Assertions.assertTrue(Utility.equalsElement(List.of(Utility.ofMap("c", "cc", "d", "dd"), Utility.ofMap("a", "aa", "b", "bb")), col));
source.del("sets3");
source.del("sets4");
source.del("sets5");
source.sadd("sets3", String.class, "setvals1");
source.sadd("sets3", String.class, "setvals2");
source.sadd("sets3", String.class, "setvals1");
source.sadd("sets4", String.class, "setvals2");
source.sadd("sets4", String.class, "setvals1");
col = source.smembersString("sets3");
System.out.println("[两值] sets3 VALUES : " + col);
List col2 = new ArrayList(col);
Collections.sort(col2);
Assertions.assertIterableEquals(List.of("setvals1", "setvals2"), col2);
bool = source.exists("sets3");
System.out.println("[有值] sets3 EXISTS : " + bool);
Assertions.assertTrue(bool);
bool = source.sismember("sets3", String.class, "setvals2");
System.out.println("[有值] sets3-setvals2 EXISTSITEM : " + bool);
Assertions.assertTrue(bool);
bool = source.sismember("sets3", String.class, "setvals3");
System.out.println("[无值] sets3-setvals3 EXISTSITEM : " + bool);
Assertions.assertFalse(bool);
source.srem("sets3", String.class, "setvals1");
col = source.smembersString("sets3");
System.out.println("[一值] sets3 VALUES : " + col);
Assertions.assertIterableEquals(List.of("setvals2"), col);
long size = source.scard("sets3");
System.out.println("sets3 大小 : " + size);
Assertions.assertEquals(1, size);
col = source.keys();
System.out.println("all keys: " + col);
col = source.keys("key*");
Collections.sort((List<String>) col);
System.out.println("key startkeys: " + col);
Assertions.assertIterableEquals(List.of("key1", "keylong1", "keys3", "keys3-2", "keystr1"), col);
num = source.incr("newnum");
System.out.println("newnum 值 : " + num);
Assertions.assertEquals(1, num);
num = source.decr("newnum");
System.out.println("newnum 值 : " + num);
Assertions.assertEquals(0, num);
Map<String, Collection> mapcol = new LinkedHashMap<>();
mapcol.put("sets3", source.smembersString("sets3"));
mapcol.put("sets4", source.smembersString("sets4"));
System.out.println("sets3&sets4: " + mapcol);
Map<String, Collection> news = new HashMap<>();
mapcol.forEach((x, y) -> {
if (y instanceof Set) {
List newy = new ArrayList(y);
Collections.sort(newy);
news.put(x, newy);
} else {
Collections.sort((List) y);
}
});
mapcol.putAll(news);
Assertions.assertEquals(Utility.ofMap("sets3", List.of("setvals2"), "sets4", List.of("setvals1", "setvals2")).toString(), mapcol.toString());
source.del("sets3");
source.del("sets4");
source.del("sets5");
source.del("sets6");
source.saddString("sets3", "setvals1", "setvals2", "setvals3", "setvals4", "setvals5");
source.saddString("sets4", "setvals3", "setvals6", "setvals7", "setvals8");
source.saddString("sets5", "setvals5", "setvals6", "setvals7", "setvals8");
Set<String> diffanswer = new TreeSet<>(Set.of("setvals1", "setvals2", "setvals4"));
Set<String> diffset = new TreeSet<>(source.sdiffString("sets3", "sets4", "sets5"));
System.out.println("sdiff: " + diffset);
Assertions.assertIterableEquals(diffanswer, diffset);
source.sdiffstore("sets6", "sets3", "sets4", "sets5");
diffset = new TreeSet<>(source.smembersString("sets6"));
System.out.println("sdiffstore: " + diffset);
Assertions.assertIterableEquals(diffanswer, diffset);
source.del("sets3");
source.del("sets4");
source.del("sets5");
source.del("sets6");
source.saddString("sets3", "setvals1", "setvals2", "setvals3", "setvals4", "setvals5");
source.saddString("sets4", "setvals2", "setvals3", "setvals5", "setvals8");
source.saddString("sets5", "setvals5", "setvals6", "setvals7", "setvals8");
Set<String> interanswer = new TreeSet<>(Set.of("setvals5"));
Set<String> interset = new TreeSet<>(source.sinterString("sets3", "sets4", "sets5"));
System.out.println("sinter: " + interset);
Assertions.assertIterableEquals(interanswer, interset);
source.sinterstore("sets6", "sets3", "sets4", "sets5");
interset = new TreeSet<>(source.smembersString("sets6"));
System.out.println("sinterstore: " + interset);
Assertions.assertIterableEquals(interanswer, interset);
source.del("sets6");
Set<String> unionanswer = new TreeSet<>(Set.of("setvals1", "setvals2", "setvals3", "setvals4", "setvals5", "setvals6", "setvals7", "setvals8"));
Set<String> unionset = new TreeSet<>(source.sunionString("sets3", "sets4", "sets5"));
System.out.println("sunion: " + unionset);
Assertions.assertIterableEquals(unionanswer, unionset);
source.sunionstore("sets6", "sets3", "sets4", "sets5");
unionset = new TreeSet<>(source.smembersString("sets6"));
System.out.println("sunionstore: " + unionset);
Assertions.assertIterableEquals(unionanswer, unionset);
List<Boolean> ems = source.smismembers("sets3", "setvals3", "setvals33");
System.out.println("smismembers: " + ems);
Assertions.assertIterableEquals(List.of(true, false), ems);
List<String> rands = List.of("setvals1", "setvals2", "setvals3", "setvals4", "setvals5");
List<String> rand2 = (List) source.srandmemberString("sets3", 100);
Collections.sort(rand2);
System.out.println("srandmember: " + rand2);
Assertions.assertIterableEquals(rands, rand2);
Assertions.assertTrue(source.smoveString("sets4", "sets5", "setvals5"));
Assertions.assertTrue(source.smoveString("sets4", "sets7", "setvals3"));
System.out.println("------------------------------------");
InetSocketAddress addr88 = new InetSocketAddress("127.0.0.1", 7788);
InetSocketAddress addr99 = new InetSocketAddress("127.0.0.1", 7799);
source.set("myaddr", InetSocketAddress.class, addr88);
obj = source.getString("myaddr");
System.out.println("myaddrstr: " + obj);
Assertions.assertEquals("127.0.0.1:7788", obj);
obj = source.get("myaddr", InetSocketAddress.class);
System.out.println("myaddr: " + obj);
Assertions.assertEquals(addr88, obj);
source.del("myaddrs");
source.del("myaddrs2");
source.sadd("myaddrs", InetSocketAddress.class, addr88);
source.sadd("myaddrs", InetSocketAddress.class, addr99);
col = source.smembers("myaddrs", InetSocketAddress.class);
System.out.println("myaddrs: " + col);
List cola2 = new ArrayList(col);
Collections.sort(cola2, (o1, o2) -> o1.toString().compareTo(o2.toString()));
Assertions.assertIterableEquals(cola2, List.of(addr88, addr99));
source.srem("myaddrs", InetSocketAddress.class, addr88);
col = source.smembers("myaddrs", InetSocketAddress.class);
System.out.println("myaddrs: " + col);
Assertions.assertIterableEquals(col, List.of(addr99));
source.sadd("myaddrs2", InetSocketAddress.class, addr88);
source.sadd("myaddrs2", InetSocketAddress.class, addr99);
mapcol.clear();
mapcol.put("myaddrs", source.smembers("myaddrs", InetSocketAddress.class));
mapcol.put("myaddrs2", source.smembers("myaddrs2", InetSocketAddress.class));
System.out.println("myaddrs&myaddrs2: " + mapcol);
Map<String, Collection> news2 = new HashMap<>();
mapcol.forEach((x, y) -> {
if (y instanceof Set) {
List newy = new ArrayList(y);
Collections.sort(newy, (o1, o2) -> o1.toString().compareTo(o2.toString()));
news2.put(x, newy);
} else {
Collections.sort((List) y, (o1, o2) -> o1.toString().compareTo(o2.toString()));
}
});
mapcol.putAll(news2);
Assertions.assertEquals(Utility.ofMap("myaddrs", List.of(addr99), "myaddrs2", List.of(addr88, addr99)).toString(), mapcol.toString());
System.out.println("------------------------------------");
source.del("myaddrs");
Type mapType = new TypeToken<Map<String, Integer>>() {
}.getType();
Map<String, Integer> paramap = new HashMap<>();
paramap.put("a", 1);
paramap.put("b", 2);
source.set("mapvals", mapType, paramap);
map = source.get("mapvals", mapType);
System.out.println("mapvals: " + map);
Assertions.assertEquals(Utility.ofMap("a", 1, "b", 2).toString(), map.toString());
source.del("hmapall");
source.hmset("hmapall", Utility.ofMap("k1", "111", "k2", "222"));
Assertions.assertIterableEquals(List.of("111", "222"), source.hvals("hmapall", String.class));
Assertions.assertIterableEquals(List.of("111", "222"), source.hvalsString("hmapall"));
Assertions.assertIterableEquals(List.of(111L, 222L), source.hvalsLong("hmapall"));
Assertions.assertIterableEquals(List.of("111", "222"), source.hvalsAsync("hmapall", String.class).join());
Assertions.assertIterableEquals(List.of("111", "222"), source.hvalsStringAsync("hmapall").join());
Assertions.assertIterableEquals(List.of(111L, 222L), source.hvalsLongAsync("hmapall").join());
Assertions.assertEquals(Utility.ofMap("k1", "111", "k2", "222"), source.hgetall("hmapall", String.class));
Assertions.assertEquals(Utility.ofMap("k1", "111", "k2", "222"), source.hgetallString("hmapall"));
Assertions.assertEquals(JsonConvert.root().convertTo(Utility.ofMap("k1", 111L, "k2", 222L)), JsonConvert.root().convertTo(source.hgetallLong("hmapall")));
Assertions.assertEquals(JsonConvert.root().convertTo(Utility.ofMap("k1", "111", "k2", "222")), JsonConvert.root().convertTo(source.hgetallAsync("hmapall", String.class).join()));
Assertions.assertEquals(JsonConvert.root().convertTo(Utility.ofMap("k1", "111", "k2", "222")), JsonConvert.root().convertTo(source.hgetallStringAsync("hmapall").join()));
Assertions.assertEquals(JsonConvert.root().convertTo(Utility.ofMap("k1", 111L, "k2", 222L)), JsonConvert.root().convertTo(source.hgetallLongAsync("hmapall").join()));
//h
source.del("hmap");
source.hincr("hmap", "key1");
num = source.hgetLong("hmap", "key1", -1);
System.out.println("hmap.key1 值 : " + num);
Assertions.assertEquals(1L, num);
source.hmset("hmap", Utility.ofMap("key2", "haha", "key3", 333));
source.hmset("hmap", "sm", (HashMap) Utility.ofMap("a", "aa", "b", "bb"));
map = source.hget("hmap", "sm", JsonConvert.TYPE_MAP_STRING_STRING);
System.out.println("hmap.sm 值 : " + map);
Assertions.assertEquals(Utility.ofMap("a", "aa", "b", "bb").toString(), map.toString());
col = source.hmget("hmap", String.class, "key1", "key2", "key3");
System.out.println("hmap.[key1,key2,key3] 值 : " + col);
Assertions.assertIterableEquals(col, List.of("1", "haha", "333"));
col = source.hkeys("hmap");
System.out.println("hmap.keys 四值 : " + col);
Assertions.assertIterableEquals(col, List.of("key1", "key2", "key3", "sm"));
source.hdel("hmap", "key1", "key3");
col = source.hkeys("hmap");
System.out.println("hmap.keys 两值 : " + col);
Assertions.assertIterableEquals(col, List.of("key2", "sm"));
obj = source.hgetString("hmap", "key2");
System.out.println("hmap.key2 值 : " + obj);
Assertions.assertEquals("haha", obj);
size = source.hlen("hmap");
System.out.println("hmap列表(2)大小 : " + size);
Assertions.assertEquals(2, size);
source.del("hmaplong");
source.hincrby("hmaplong", "key1", 10);
source.hsetLong("hmaplong", "key2", 30);
AtomicLong cursor = new AtomicLong();
Map<String, Long> longmap = source.hscan("hmaplong", long.class, cursor, 10);
System.out.println("hmaplong.所有两值 : " + longmap);
Assertions.assertEquals(Utility.ofMap("key1", 10, "key2", 30).toString(), longmap.toString());
Assertions.assertEquals(2L, source.hstrlen("hmaplong", "key1"));
source.del("hmapstr");
source.hsetString("hmapstr", "key1", "str10");
source.hsetString("hmapstr", "key2", null);
cursor = new AtomicLong();
map = source.hscan("hmapstr", String.class, cursor, 10);
System.out.println("hmapstr.所有一值 : " + map);
Assertions.assertEquals(Utility.ofMap("key1", "str10").toString(), map.toString());
source.del("hmapstrmap");
source.hset("hmapstrmap", "key1", JsonConvert.TYPE_MAP_STRING_STRING, (HashMap) Utility.ofMap("ks11", "vv11"));
source.hset("hmapstrmap", "key2", JsonConvert.TYPE_MAP_STRING_STRING, null);
cursor = new AtomicLong();
map = source.hscan("hmapstrmap", JsonConvert.TYPE_MAP_STRING_STRING, cursor, 10, "key2*");
System.out.println("hmapstrmap.无值 : " + map);
Assertions.assertEquals(Utility.ofMap().toString(), map.toString());
source.del("hmap");
String vpref = "v";
int ccc = 600;
for (int i = 101; i <= ccc + 100; i++) {
source.hmset("hmap", "k" + i, vpref + i);
}
cursor = new AtomicLong();
Map<String, String> smap = source.hscan("hmap", String.class, cursor, 5);
System.out.println("hmap.hscan 长度 : " + smap.size() + ", cursor: " + cursor + ", 内容: " + smap);
//smap.size 是不确定的可能是全量也可能比5多也可能比5少
Assertions.assertFalse(smap.isEmpty());
if (smap.size() == ccc) {
Assertions.assertTrue(cursor.get() == 0);
} else {
Assertions.assertTrue(cursor.get() > 0);
}
cursor = new AtomicLong();
smap = (Map) source.hscanAsync("hmap", String.class, cursor, 5).join();
Assertions.assertFalse(smap.isEmpty());
if (smap.size() == ccc) {
Assertions.assertTrue(cursor.get() == 0);
} else {
Assertions.assertTrue(cursor.get() > 0);
}
source.del("sortset");
source.zadd("sortset", 100, "key100");
source.zadd("sortset", 200, "key200");
source.zadd("sortset", 300, "key300");
source.zadd("sortset", 400, "key400");
source.zadd("sortset", 500, "key500");
System.out.println("sortset 写入5条记录 ");
Assertions.assertEquals(2L, source.zrank("sortset", "key300"));
Assertions.assertEquals(1L, source.zrevrank("sortset", "key400"));
Assertions.assertEquals(List.of("key100", "key200", "key300"), source.zrange("sortset", 0, 2));
cursor = new AtomicLong();
Assertions.assertEquals(List.of(CacheScoredValue.create(100, "key100"),
CacheScoredValue.create(200, "key200"),
CacheScoredValue.create(300, "key300"),
CacheScoredValue.create(400, "key400"),
CacheScoredValue.create(500, "key500")
), source.zscanInteger("sortset", cursor, -1));
size = source.zcard("sortset");
Assertions.assertEquals(5, size);
size = source.zrem("sortset", "key400", "key800");
Assertions.assertTrue(size == 1);
List<Long> scores = source.zmscoreLong("sortset", "key200", "key800", "key500");
List<Long> scoreAnswer = new ArrayList<>();
scoreAnswer.add(200L);
scoreAnswer.add(null);
scoreAnswer.add(500L);
Assertions.assertIterableEquals(scoreAnswer, scores);
source.del("sortnumset");
source.zincrby("sortnumset", 100, "int100");
Assertions.assertEquals(100, source.zscoreInteger("sortnumset", "int100"));
source.zincrby("sortnumset", 120.12f, "float120");
source.zincrby("sortnumset", 130.13f, "float120");
Assertions.assertEquals(250.25f, source.zscore("sortnumset", float.class, "float120"));
source.del("popset");
source.saddString("popset", "111");
source.saddString("popset", "222");
source.saddString("popset", "333");
source.saddString("popset", "444");
source.saddString("popset", "555");
cursor = new AtomicLong();
Set<String> sset = source.sscan("popset", String.class, cursor, 3);
System.out.println("popset.sscan 长度 : " + sset.size() + ", cursor: " + cursor + ", 内容: " + sset);
//smap.size 是不确定的可能是全量也可能比5多也可能比5少
Assertions.assertFalse(sset.isEmpty());
if (sset.size() == 5) {
Assertions.assertTrue(cursor.get() == 0);
} else {
Assertions.assertTrue(cursor.get() > 0);
}
cursor = new AtomicLong();
sset = (Set) source.sscanAsync("popset", String.class, cursor, 3).join();
Assertions.assertFalse(smap.isEmpty());
if (sset.size() == 5) {
Assertions.assertTrue(cursor.get() == 0);
} else {
Assertions.assertTrue(cursor.get() > 0);
}
obj = source.spopString("popset");
Assertions.assertTrue(obj != null);
System.out.println("SPOP一个String元素" + obj);
Assertions.assertTrue(List.of("111", "222", "333", "444", "555").contains(obj));
size = source.scard("popset");
System.out.println("popset元素个数" + size);
Assertions.assertEquals(4, size);
col = source.spopString("popset", 2);
Assertions.assertEquals(2, col.size());
System.out.println("SPOP两个String元素" + col);
col = source.spopString("popset", 5);
Assertions.assertEquals(2, col.size());
System.out.println("SPOP五个String元素(值两个)" + col);
source.del("popset");
source.saddLong("popset", 111L);
source.saddLong("popset", 222L);
source.saddLong("popset", 333L);
source.saddLong("popset", 444L, 555L);
System.out.println("SPOP一个Long元素" + source.spopLong("popset"));
col = source.spopLong("popset", 2);
Assertions.assertEquals(2, col.size());
System.out.println("SPOP两个Long元素" + col);
col = source.spopLong("popset", 5);
Assertions.assertEquals(2, col.size());
System.out.println("SPOP五个Long元素(值两个)" + col);
obj = source.spopLong("popset");
Assertions.assertTrue(obj == null);
System.out.println("SPOP一个Long元素" + obj);
cursor = new AtomicLong();
List<String> keys = source.scan(cursor, 5);
System.out.println("scan 长度 : " + keys.size() + ", dbsize: " + source.dbsize() + ", cursor: " + cursor + ", 内容: " + keys);
Assertions.assertFalse(keys.isEmpty());
if (keys.size() == source.dbsize()) {
Assertions.assertTrue(cursor.get() == 0);
} else {
Assertions.assertTrue(cursor.get() > 0);
}
cursor = new AtomicLong();
keys = (List) source.scanAsync(cursor, 5).join();
Assertions.assertFalse(keys.isEmpty());
long dbsize = source.dbsize();
System.out.println("keys总数量 : " + dbsize);
//清除
long rs = source.del("stritem1");
System.out.println("删除stritem1个数: " + rs);
source.del("popset");
source.del("stritem2");
source.del("intitem1");
source.del("intitem2");
source.del("keylong1");
source.del("keystr1");
source.del("mapvals");
source.del("myaddr");
source.del("myaddrs2");
source.del("newnum");
source.del("objitem1");
source.del("objitem2");
source.del("key1");
source.del("key2");
source.del("keys3", "keys3-2");
source.del("sets3", "sets4", "sets5", "sets6");
source.del("myaddrs");
source.del("300");
source.del("stringmap");
source.del("hmap");
source.del("hmapall");
source.del("hmaplong");
source.del("hmapstr");
source.del("hmapstrmap");
source.del("byteskey");
source.del("nxexkey1");
System.out.println("--------###------- 接口测试结束 --------###-------");
source.flushdb();
System.out.println("------订阅发布功能------");
final CountDownLatch cdl = new CountDownLatch(2);
final String channel = "hello_topic";
final String content = "this is a message content";
CacheEventListener<byte[]> listener = new CacheEventListener<byte[]>() {
@Override
public void onMessage(String topic, byte[] message) {
String msg = new String(message, StandardCharsets.UTF_8);
System.out.println("订阅到主题: " + topic + ", 消息: " + msg);
Assertions.assertEquals(channel, topic);
Assertions.assertEquals(content, msg);
cdl.countDown();
}
};
source.subscribe(listener, channel);
System.out.println("订阅结束");
source.publish(channel, content);
System.out.println("发布结束");
if (!source.getClass().getName().contains("Redisson")) { //Redisson不支持
List<String> channels = source.pubsubChannels(null);
Assertions.assertEquals(List.of(channel), channels);
}
source.unsubscribe(listener, channel);
System.out.println("取消订阅结束");
source.publish(channel, content);
System.out.println("再次发布结束");
source.subscribe(listener, channel);
System.out.println("再次订阅结束");
source.publish(channel, content);
System.out.println("再次发布结束");
cdl.await();
source.unsubscribe(listener, channel);
System.out.println("取消订阅结束");
}
}