[Add] 增加自动清理机制;

[Add] AutoCleanTimer, Cleanable 增加定时清理器和所需实现的接口;
[Update] HashCacheStore 实现了 Cleanable 接口, 并使用了一种简单但效率较低的遍历清除方法, 有待改进;
This commit is contained in:
LamGC 2020-09-04 23:36:23 +08:00
parent 565049f393
commit d650f12626
Signed by: LamGC
GPG Key ID: 6C5AE2A913941E1D
3 changed files with 119 additions and 1 deletions

View File

@ -0,0 +1,85 @@
package net.lamgc.cgj.bot.cache.local;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 定时清理机制.
* <p>定时通知已实现 {@link Cleanable} 接口的对象进行清理.
* @see Cleanable
* @author LamGC
*/
public class AutoCleanTimer implements Runnable {
private final static Set<Cleanable> CLEANABLE_STORE_SET = new CopyOnWriteArraySet<>();
private final static ScheduledExecutorService SCHEDULED_EXECUTOR =
new ScheduledThreadPoolExecutor(1,
new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("Thread-AutoClean-%d")
.build()
);
private final static Logger log = LoggerFactory.getLogger(AutoCleanTimer.class);
private final static AtomicBoolean DEBUG_ENABLE = new AtomicBoolean(false);
static {
SCHEDULED_EXECUTOR.scheduleAtFixedRate(new AutoCleanTimer(), 100L, 100L, TimeUnit.MILLISECONDS);
Thread shutdownHook = new Thread(SCHEDULED_EXECUTOR::shutdown);
shutdownHook.setName("ShutdownThread-AutoClean");
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
/**
* 是否启用调试模式.
* <p>启用后将会打印相关日志.
* @param enable 是否启用调试模式.
*/
public static void setDebugEnable(boolean enable) {
DEBUG_ENABLE.set(enable);
}
/**
* 增加需要定时执行清理的缓存库
* @param store 已实现Cleanable的对象
*/
public static void add(Cleanable store) {
CLEANABLE_STORE_SET.add(store);
}
/**
* 移除已添加的缓存库
* @param store 需要从AutoCleanTimer移除的对象
*/
public static void remove(Cleanable store) {
CLEANABLE_STORE_SET.remove(store);
}
private AutoCleanTimer() {}
@Override
public void run() {
if (CLEANABLE_STORE_SET.size() == 0) {
return;
}
CLEANABLE_STORE_SET.forEach(cleanable -> {
try {
cleanable.clean();
} catch (Exception e) {
log.error("{} 执行清理动作时发生异常:\n{}", cleanable.toString(), Throwables.getStackTraceAsString(e));
}
});
}
}

View File

@ -0,0 +1,16 @@
package net.lamgc.cgj.bot.cache.local;
/**
* 可清理接口, 实现该接口代表该类具有清理动作.
* @author LamGC
*/
public interface Cleanable {
/**
* 该方法需要CacheStore完成对过期Entry的清除.
* @return 返回已清理数量.
* @throws Exception 即使该方法抛出异常, 也不会影响后续情况.
*/
long clean() throws Exception;
}

View File

@ -20,6 +20,7 @@ package net.lamgc.cgj.bot.cache.local;
import net.lamgc.cgj.bot.cache.CacheStore;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* 基于 {@link Hashtable} 的缓存存储容器.
@ -28,7 +29,7 @@ import java.util.*;
* @see net.lamgc.cgj.bot.cache.CacheStore
* @see Hashtable
*/
public abstract class HashCacheStore<V> implements CacheStore<V> {
public abstract class HashCacheStore<V> implements CacheStore<V>, Cleanable {
private final Map<String, CacheItem<V>> cacheMap = new Hashtable<>();
@ -100,6 +101,22 @@ public abstract class HashCacheStore<V> implements CacheStore<V> {
return Collections.unmodifiableSet(cacheMap.keySet());
}
@Override
public long clean() {
Map<String, CacheItem<V>> cacheMap = getCacheMap();
Date currentDate = new Date();
AtomicLong cleanCount = new AtomicLong(0);
cacheMap.keySet().removeIf(key -> {
CacheItem<V> item = cacheMap.get(key);
if (item != null && item.isExpire(currentDate)) {
cleanCount.incrementAndGet();
return true;
}
return false;
});
return cleanCount.get();
}
/**
* 缓存项.
* @author LamGC