[Add] 增加新的缓存库类(HotDataCacheStore, StringRedisCacheStore);

[Add] CacheStore 增加update(String, T, long)方法, 该方法将以Unix时间戳设置过期时间;
[Update] LocalHashCacheStore, RedisPoolCacheStore 适配CacheStore新方法;
[Delete] 删除 RedisCacheStore 类;
This commit is contained in:
LamGC 2020-04-11 12:13:55 +08:00
parent 8375b81b17
commit a0efc3c0af
6 changed files with 161 additions and 113 deletions

View File

@ -4,6 +4,14 @@ import java.util.Date;
public interface CacheStore<T> {
/**
* 更新或添加缓存项
* @param key 缓存键名
* @param value 缓存值
* @param expire 有效期, 单位为ms(毫秒), 如不过期传入0或赋值
*/
void update(String key, T value, long expire);
/**
* 更新或添加缓存项
* @param key 缓存键名

View File

@ -0,0 +1,96 @@
package net.lamgc.cgj.bot.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.Objects;
import java.util.Random;
/**
* 具有继承性的热点数据缓存库
* @param <T> 存储类型
* @author LamGC
*/
public class HotDataCacheStore<T> implements CacheStore<T> {
private final CacheStore<T> parent;
private final CacheStore<T> current;
private final long expireTime;
private final int expireFloatRange;
private final Random random = new Random();
private final Logger log = LoggerFactory.getLogger(HotDataCacheStore.class.getSimpleName() + "@" + Integer.toHexString(this.hashCode()));
/**
* 构造热点缓存存储对象
* @param parent 上级缓存存储库
* @param current 热点缓存存储库, 最好使用本地缓存(例如 {@linkplain LocalHashCacheStore LocalHashCacheStore})
* @param expireTime 本地缓存库的缓存项过期时间,
* 该时间并不是所有缓存项的最终过期时间, 还需要根据expireFloatRange的设定随机设置, 公式:
* {@code expireTime + new Random().nextInt(expireFloatRange)}
* @param expireFloatRange 过期时间的浮动范围, 用于防止短时间内大量缓存项失效导致的缓存雪崩
*/
public HotDataCacheStore(CacheStore<T> parent, CacheStore<T> current, long expireTime, int expireFloatRange) {
this.parent = parent;
this.current = current;
this.expireTime = expireTime;
this.expireFloatRange = expireFloatRange;
log.debug("HotDataCacheStore初始化完成. (Parent: {}, Current: {}, expireTime: {}, expireFloatRange: {})",
parent, current, expireTime, expireFloatRange);
}
@Override
public void update(String key, T value, long expire) {
update(key, value, expire <= 0 ? null : new Date(System.currentTimeMillis() + expire));
}
@Override
public void update(String key, T value, Date expire) {
parent.update(key, value, expire);
current.update(key, value, expire);
}
@Override
public T getCache(String key) {
if(!exists(key)) {
log.debug("查询缓存键名不存在, 直接返回null.");
return null;
}
T result = current.getCache(key);
if(Objects.isNull(result)) {
log.debug("Current缓存库未命中, 查询Parent缓存库");
T parentResult = parent.getCache(key);
if(Objects.isNull(parentResult)) {
log.debug("Parent缓存库未命中, 缓存不存在");
return null;
}
log.debug("Parent缓存命中, 正在更新Current缓存库...");
current.update(key, parentResult, expireTime + random.nextInt(expireFloatRange));
log.debug("Current缓存库更新完成.");
result = parentResult;
} else {
log.debug("Current缓存库缓存命中.");
}
return result;
}
@Override
public boolean exists(String key) {
return current.exists(key) || parent.exists(key);
}
@Override
public boolean exists(String key, Date date) {
return current.exists(key, date) || parent.exists(key, date);
}
@Override
public boolean clear() {
return current.clear();
}
@Override
public boolean supportedPersistence() {
return current.supportedPersistence() || parent.supportedPersistence();
}
}

View File

@ -28,6 +28,11 @@ public class LocalHashCacheStore<T> implements CacheStore<T> {
}
}
@Override
public void update(String key, T value, long expire) {
update(key, value, expire <= 0 ? null : new Date(System.currentTimeMillis() + expire));
}
@Override
public void update(String key, T value, Date expire) {
if(cache.containsKey(key)) {

View File

@ -1,100 +0,0 @@
package net.lamgc.cgj.bot.cache;
import com.google.common.base.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisConnectionException;
import java.net.URI;
import java.util.Date;
public abstract class RedisCacheStore<T> implements CacheStore<T> {
private final Jedis jedis;
private final Logger log;
private final String keyPrefix;
public RedisCacheStore(URI redisServerUri, String prefix) {
this(redisServerUri, null, prefix);
}
/**
* 创建一个Redis缓存数据库对象
* @param redisServerUri 数据库链接
* @param password 登录密码(如果有)
* @throws JedisConnectionException 当连接失败时抛出
*/
public RedisCacheStore(URI redisServerUri, String password, String prefix) throws JedisConnectionException {
this.jedis = new Jedis(redisServerUri.getHost(), redisServerUri.getPort() <= 0 ? 6379 : redisServerUri.getPort());
log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(jedis.hashCode()));
log.info("Redis数据库连接状态: {}", jedis.ping());
if(password != null) {
this.jedis.auth(password);
}
if(!Strings.isNullOrEmpty(prefix)) {
keyPrefix = prefix.endsWith(".") ? prefix : prefix + ".";
} else {
keyPrefix = "";
}
}
public void connect() {
if(!"PONE".equals(jedis.ping())) {
jedis.connect();
}
}
@Override
public void update(String key, T value, Date expire) {
Transaction multi = jedis.multi();
multi.set(keyPrefix + key, parse(value));
if(expire != null) {
multi.expireAt(keyPrefix + key, expire.getTime());
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
}
multi.exec();
}
@Override
public T getCache(String key) {
return analysis(jedis.get(keyPrefix + key));
}
@Override
public boolean exists(String key) {
return jedis.exists(keyPrefix + key);
}
@Override
public boolean exists(String key, Date date) {
return exists(key);
}
@Override
public boolean clear() {
String result = jedis.flushDB();
log.info("flushDB返回结果: {}", result);
return true;
}
/**
* 转换方法
* @param dataObj 原数据
* @return 文本型数据
*/
protected abstract String parse(T dataObj);
/**
* 将String数据转换成指定类型的对象
* @param dataStr String数据
* @return 泛型指定类型的对象
*/
protected abstract T analysis(String dataStr);
@Override
public boolean supportedPersistence() {
return true;
}
}

View File

@ -21,15 +21,11 @@ public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
}
public RedisPoolCacheStore(URI redisServerUri, JedisPoolConfig config, int timeout, String password, String prefix) {
jedisPool = new JedisPool(config == null ? new GenericObjectPoolConfig<JedisPool>() : config, redisServerUri.getHost(),
redisServerUri.getPort() <= 0 ? 6379 : redisServerUri.getPort(),
timeout <= 0 ? Protocol.DEFAULT_TIMEOUT : timeout, password);
log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(jedisPool.hashCode()));
if(prefix != null) {
keyPrefix = prefix.endsWith(".") ? prefix : prefix + ".";
} else {
keyPrefix = "";
}
this(new JedisPool(config == null ? new GenericObjectPoolConfig<JedisPool>() : config, redisServerUri.getHost(),
redisServerUri.getPort() == -1 ? 6379 : redisServerUri.getPort(),
timeout <= 0 ? Protocol.DEFAULT_TIMEOUT : timeout, password),
prefix
);
}
public RedisPoolCacheStore(JedisPool pool, String keyPrefix) {
@ -45,16 +41,19 @@ public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
}
}
@Override
public void update(String key, T value, long expire) {
update(key, value, expire <= 0 ? null : new Date(System.currentTimeMillis() + expire));
}
@Override
public void update(String key, T value, Date expire) {
Jedis jedis = jedisPool.getResource();
Transaction multi = jedis.multi();
multi.set(keyPrefix + key, parse(value));
jedis.set(keyPrefix + key, parse(value));
if(expire != null) {
multi.expireAt(keyPrefix + key, expire.getTime());
jedis.pexpireAt(keyPrefix + key, expire.getTime());
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
}
multi.exec();
jedis.close();
}
@ -106,4 +105,14 @@ public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
public boolean supportedPersistence() {
return true;
}
/**
* 替换原本的分隔符(.)(:).<br/>
* 即将启用
* @param key 要处理的键名
* @return 处理后的键名
*/
public static String replaceKey(String key) {
return key.replaceAll("\\.", ":");
}
}

View File

@ -0,0 +1,30 @@
package net.lamgc.cgj.bot.cache;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import java.net.URI;
public class StringRedisCacheStore extends RedisPoolCacheStore<String> {
public StringRedisCacheStore(URI redisServerUri, String prefix) {
super(redisServerUri, prefix);
}
public StringRedisCacheStore(URI redisServerUri, JedisPoolConfig config, int timeout, String password, String prefix) {
super(redisServerUri, config, timeout, password, prefix);
}
public StringRedisCacheStore(JedisPool pool, String keyPrefix) {
super(pool, keyPrefix);
}
@Override
protected String parse(String dataObj) {
return dataObj;
}
@Override
protected String analysis(String dataStr) {
return dataStr;
}
}