[Add] AutoCleanTimer, Cleanable 增加缓存库自动清理;

[Update] HotDataCacheStore 增加对自动清理的支持;
[Change] RedisPoolCacheStore 将Redis缓存库抽象类设为default(原public);
[Change] MiraiMessageSender, BotCommandProcess 适配HotDataCacheStore的修改;
[Change] BotCommandProcess 调整ranking命令的输出格式;
This commit is contained in:
2020-05-07 10:26:11 +08:00
parent a36eb713d7
commit aabd35ce71
6 changed files with 87 additions and 11 deletions

View File

@ -50,7 +50,7 @@ public class BotCommandProcess {
private final static CacheStore<JsonElement> illustInfoCache = new JsonRedisCacheStore(BotEventHandler.redisServer, "illustInfo", gson); private final static CacheStore<JsonElement> illustInfoCache = new JsonRedisCacheStore(BotEventHandler.redisServer, "illustInfo", gson);
private final static CacheStore<JsonElement> illustPreLoadDataCache = new HotDataCacheStore<>( private final static CacheStore<JsonElement> illustPreLoadDataCache = new HotDataCacheStore<>(
new JsonRedisCacheStore(BotEventHandler.redisServer, "illustPreLoadData", gson), new JsonRedisCacheStore(BotEventHandler.redisServer, "illustPreLoadData", gson),
new LocalHashCacheStore<>(), 3600000, 900000); new LocalHashCacheStore<>(), 3600000, 900000, true);
private final static CacheStore<JsonElement> searchBodyCache = new JsonRedisCacheStore(BotEventHandler.redisServer, "searchBody", gson); private final static CacheStore<JsonElement> searchBodyCache = new JsonRedisCacheStore(BotEventHandler.redisServer, "searchBody", gson);
private final static CacheStore<List<JsonObject>> rankingCache = new JsonObjectRedisListCacheStore(BotEventHandler.redisServer, "ranking", gson); private final static CacheStore<List<JsonObject>> rankingCache = new JsonObjectRedisListCacheStore(BotEventHandler.redisServer, "ranking", gson);
private final static CacheStore<List<String>> pagesCache = new StringListRedisCacheStore(BotEventHandler.redisServer, "imagePages"); private final static CacheStore<List<String>> pagesCache = new StringListRedisCacheStore(BotEventHandler.redisServer, "imagePages");
@ -408,7 +408,7 @@ public class BotCommandProcess {
StringBuilder builder = new StringBuilder("["); StringBuilder builder = new StringBuilder("[");
illustObj.get("tags").getAsJsonArray().forEach(el -> builder.append(el.getAsString()).append(", ")); illustObj.get("tags").getAsJsonArray().forEach(el -> builder.append(el.getAsString()).append(", "));
builder.replace(builder.length() - 2, builder.length(), "]"); builder.replace(builder.length() - 2, builder.length(), "]");
log.debug("{} ({} / {})\n\t作品id: {}, \n\t作者名(作者id): {} ({}), \n\t作品标题: {}, \n\t作品Tags: {}, \n\t页数: {}, \n\t作品链接: {}", log.debug("{} ({} / {})\n\t作品id: {}, \n\t作者名(作者id): {} ({}), \n\t作品标题: {}, \n\t作品Tags: {}, \n\t页数: {}, \n\t作品链接: {}",
searchArea.name(), searchArea.name(),
count, count,
illustsList.size(), illustsList.size(),
@ -435,7 +435,7 @@ public class BotCommandProcess {
result.append(searchArea.name()).append(" (").append(count).append(" / ").append(limit).append(")\n\t作品id: ").append(illustId) result.append(searchArea.name()).append(" (").append(count).append(" / ").append(limit).append(")\n\t作品id: ").append(illustId)
.append(", \n\t作者名: ").append(illustObj.get("userName").getAsString()) .append(", \n\t作者名: ").append(illustObj.get("userName").getAsString())
.append("\n\t作品标题: ").append(illustObj.get("illustTitle").getAsString()) .append("\n\t作品标题: ").append(illustObj.get("illustTitle").getAsString())
.append("\n\t作品页数: ").append(illustObj.get("pageCount").getAsInt()) .append("\n\t作品页数: ").append(illustObj.get("pageCount").getAsInt()).append("")
.append("\n").append(imageMsg).append("\n"); .append("\n").append(imageMsg).append("\n");
count++; count++;
} }

View File

@ -0,0 +1,52 @@
package net.lamgc.cgj.bot.cache;
import com.google.common.base.Throwables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CopyOnWriteArraySet;
public class AutoCleanTimer extends TimerTask {
private final static Set<Cleanable> cleanSet = new CopyOnWriteArraySet<>();
private final static Timer cleanTimer = new Timer("Thread-AutoClean", true);
private final static Logger log = LoggerFactory.getLogger(AutoCleanTimer.class.getName());
static {
cleanTimer.schedule(new AutoCleanTimer(), 100L);
}
/**
* 增加需要定时执行清理的缓存库
* @param store 已实现Cleanable的对象
*/
public static void add(Cleanable store) {
cleanSet.add(store);
}
/**
* 移除已添加的缓存库
* @param store 需要从AutoCleanTimer移除的对象
*/
public static void remove(Cleanable store) {
cleanSet.remove(store);
}
private AutoCleanTimer() {}
@Override
public void run() {
cleanSet.forEach(cleanable -> {
try {
cleanable.clean();
} catch (Exception e) {
log.error("{} 执行清理动作时发生异常:\n{}", cleanable.toString(), Throwables.getStackTraceAsString(e));
}
});
}
}

View File

@ -0,0 +1,10 @@
package net.lamgc.cgj.bot.cache;
/**
* 可清理接口, 实现该接口代表该类拥有清理动作.
*/
public interface Cleanable {
void clean() throws Exception;
}

View File

@ -10,7 +10,7 @@ import java.util.*;
* @param <T> 存储类型 * @param <T> 存储类型
* @author LamGC * @author LamGC
*/ */
public class HotDataCacheStore<T> implements CacheStore<T> { public class HotDataCacheStore<T> implements CacheStore<T>, Cleanable {
private final CacheStore<T> parent; private final CacheStore<T> parent;
private final CacheStore<T> current; private final CacheStore<T> current;
@ -27,14 +27,19 @@ public class HotDataCacheStore<T> implements CacheStore<T> {
* 该时间并不是所有缓存项的最终过期时间, 还需要根据expireFloatRange的设定随机设置, 公式: * 该时间并不是所有缓存项的最终过期时间, 还需要根据expireFloatRange的设定随机设置, 公式:
* {@code expireTime + new Random().nextInt(expireFloatRange)} * {@code expireTime + new Random().nextInt(expireFloatRange)}
* @param expireFloatRange 过期时间的浮动范围(单位毫秒), 用于防止短时间内大量缓存项失效导致的缓存雪崩 * @param expireFloatRange 过期时间的浮动范围(单位毫秒), 用于防止短时间内大量缓存项失效导致的缓存雪崩
* @param autoClean 是否交由{@link AutoCleanTimer}自动执行清理
*/ */
public HotDataCacheStore(CacheStore<T> parent, CacheStore<T> current, long expireTime, int expireFloatRange) { public HotDataCacheStore(CacheStore<T> parent, CacheStore<T> current, long expireTime, int expireFloatRange, boolean autoClean) {
this.parent = parent; this.parent = parent;
this.current = current; this.current = current;
this.expireTime = expireTime; this.expireTime = expireTime;
this.expireFloatRange = expireFloatRange; this.expireFloatRange = expireFloatRange;
log.debug("HotDataCacheStore初始化完成. (Parent: {}, Current: {}, expireTime: {}, expireFloatRange: {})", if(autoClean) {
parent, current, expireTime, expireFloatRange); AutoCleanTimer.add(this);
}
log.debug("HotDataCacheStore初始化完成. (Parent: {}, Current: {}, expireTime: {}, expireFloatRange: {}, autoClean: {})",
parent, current, expireTime, expireFloatRange, autoClean);
} }
@Override @Override
@ -121,4 +126,13 @@ public class HotDataCacheStore<T> implements CacheStore<T> {
public boolean supportedList() { public boolean supportedList() {
return false; return false;
} }
@Override
public void clean() {
for(String key : this.current.keys()) {
if(current.exists(key)) {
current.remove(key);
}
}
}
} }

View File

@ -13,7 +13,7 @@ import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
public abstract class RedisPoolCacheStore<T> implements CacheStore<T> { abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
private final JedisPool jedisPool; private final JedisPool jedisPool;
private final String keyPrefix; private final String keyPrefix;

View File

@ -33,7 +33,7 @@ public class MiraiMessageSender implements MessageSender {
private final static CacheStore<String> imageIdCache = new HotDataCacheStore<>( private final static CacheStore<String> imageIdCache = new HotDataCacheStore<>(
new StringRedisCacheStore(BotEventHandler.redisServer, "mirai.imageId"), new StringRedisCacheStore(BotEventHandler.redisServer, "mirai.imageId"),
new LocalHashCacheStore<>(), new LocalHashCacheStore<>(),
5400000, 1800000); 5400000, 1800000, true);
/** /**
* 使用id构造发送器 * 使用id构造发送器