diff --git a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java new file mode 100644 index 0000000..e0c2dc9 --- /dev/null +++ b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/AutoCleanTimer.java @@ -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; + +/** + * 定时清理机制. + *

定时通知已实现 {@link Cleanable} 接口的对象进行清理. + * @see Cleanable + * @author LamGC + */ +public class AutoCleanTimer implements Runnable { + + private final static Set 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); + } + + /** + * 是否启用调试模式. + *

启用后将会打印相关日志. + * @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)); + } + }); + } +} diff --git a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/Cleanable.java b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/Cleanable.java new file mode 100644 index 0000000..7e98897 --- /dev/null +++ b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/Cleanable.java @@ -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; + +} diff --git a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/HashCacheStore.java b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/HashCacheStore.java index f92b65a..124560f 100644 --- a/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/HashCacheStore.java +++ b/ContentGrabbingJi-CacheStore-local/src/main/java/net/lamgc/cgj/bot/cache/local/HashCacheStore.java @@ -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 implements CacheStore { +public abstract class HashCacheStore implements CacheStore, Cleanable { private final Map> cacheMap = new Hashtable<>(); @@ -100,6 +101,22 @@ public abstract class HashCacheStore implements CacheStore { return Collections.unmodifiableSet(cacheMap.keySet()); } + @Override + public long clean() { + Map> cacheMap = getCacheMap(); + Date currentDate = new Date(); + AtomicLong cleanCount = new AtomicLong(0); + cacheMap.keySet().removeIf(key -> { + CacheItem item = cacheMap.get(key); + if (item != null && item.isExpire(currentDate)) { + cleanCount.incrementAndGet(); + return true; + } + return false; + }); + return cleanCount.get(); + } + /** * 缓存项. * @author LamGC