|
|
@@ -0,0 +1,211 @@
|
|
|
+package com.usthe.collector.collect.common.cache;
|
|
|
+
|
|
|
+
|
|
|
+import com.googlecode.concurrentlinkedhashmap.ConcurrentLinkedHashMap;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Optional;
|
|
|
+import java.util.concurrent.ArrayBlockingQueue;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.ScheduledThreadPoolExecutor;
|
|
|
+import java.util.concurrent.ThreadPoolExecutor;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+
|
|
|
+/**
|
|
|
+ * lru cache 对连接对象进行缓存
|
|
|
+ * @author tomsun28
|
|
|
+ * @date 2021-12-10 23:17
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class CommonCache {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认缓存时间 30minute
|
|
|
+ */
|
|
|
+ private static final long DEFAULT_CACHE_TIMEOUT = 30 * 60 * 1000L;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 默认最大缓存数量
|
|
|
+ */
|
|
|
+ private static final int DEFAULT_MAX_CAPACITY = 10000;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * cacheTime数组大小
|
|
|
+ */
|
|
|
+ private static final int CACHE_TIME_LENGTH = 2;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 存储对象的数据过期时间点
|
|
|
+ */
|
|
|
+ private Map<Object, Long[]> timeoutMap;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 存储缓存对象
|
|
|
+ */
|
|
|
+ private ConcurrentLinkedHashMap<Object, Object> cacheMap;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 过期数据清理线程池
|
|
|
+ */
|
|
|
+ private ThreadPoolExecutor cleanTimeoutExecutor;
|
|
|
+
|
|
|
+ private CommonCache() { init();}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化 cache
|
|
|
+ */
|
|
|
+ private void init() {
|
|
|
+ // 初始化lru hashmap
|
|
|
+ cacheMap = new ConcurrentLinkedHashMap
|
|
|
+ .Builder<>()
|
|
|
+ .maximumWeightedCapacity(DEFAULT_MAX_CAPACITY)
|
|
|
+ .listener((key, value) -> {
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ if (value instanceof CacheCloseable) {
|
|
|
+ ((CacheCloseable)value).close();
|
|
|
+ }
|
|
|
+ log.info("lru cache discard key: {}, value: {}.", key, value);
|
|
|
+ }).build();
|
|
|
+
|
|
|
+ // 初始化时间纪录map
|
|
|
+ timeoutMap = new ConcurrentHashMap<>(DEFAULT_MAX_CAPACITY >> 6);
|
|
|
+
|
|
|
+ // 初始化过期数据清理线程池
|
|
|
+ cleanTimeoutExecutor = new ThreadPoolExecutor(1, 1,
|
|
|
+ 1, TimeUnit.SECONDS,
|
|
|
+ new ArrayBlockingQueue<>(1), r -> new Thread("lru-cache-timeout-cleaner"),
|
|
|
+ new ThreadPoolExecutor.DiscardOldestPolicy());
|
|
|
+
|
|
|
+ // 初始化可用性探测定位任务,每次探测间隔时间为20分钟
|
|
|
+ ScheduledThreadPoolExecutor scheduledExecutor = new ScheduledThreadPoolExecutor(1,
|
|
|
+ r -> new Thread(r, "lru-cache-available-detector"));
|
|
|
+ scheduledExecutor.scheduleWithFixedDelay(this::detectCacheAvailable,
|
|
|
+ 2,20, TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 探测所有可探测的缓存对象的可用性,清除不可用和过期对象
|
|
|
+ */
|
|
|
+ private void detectCacheAvailable() {
|
|
|
+ try {
|
|
|
+ cacheMap.forEach((key, value) -> {
|
|
|
+ // 先判断是否过期
|
|
|
+ Long[] cacheTime = timeoutMap.get(key);
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+ if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH
|
|
|
+ || cacheTime[0] + cacheTime[1] < currentTime) {
|
|
|
+ cacheMap.remove(key);
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ if (value instanceof CacheCloseable) {
|
|
|
+ ((CacheCloseable)value).close();
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("detect cache available error: {}.", e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清理过期线程
|
|
|
+ */
|
|
|
+ private void cleanTimeoutCache() {
|
|
|
+ try {
|
|
|
+ cacheMap.forEach((key, value) -> {
|
|
|
+ // index 0 is startTime, 1 is timeDiff
|
|
|
+ Long[] cacheTime = timeoutMap.get(key);
|
|
|
+ long currentTime = System.currentTimeMillis();
|
|
|
+ if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH) {
|
|
|
+ timeoutMap.put(key, new Long[]{currentTime, DEFAULT_CACHE_TIMEOUT});
|
|
|
+ } else if (cacheTime[0] + cacheTime[1] < currentTime) {
|
|
|
+ // 过期了 discard 关闭这个cache的资源
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ cacheMap.remove(key);
|
|
|
+ if (value instanceof CacheCloseable) {
|
|
|
+ ((CacheCloseable)value).close();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("clean timeout cache error: {}.", e.getMessage(), e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 新增或更新cache
|
|
|
+ * @param key 存储对象key
|
|
|
+ * @param value 存储对象
|
|
|
+ * @param timeDiff 缓存对象保存时间 millis
|
|
|
+ */
|
|
|
+ public void addCache(Object key, Object value, Long timeDiff) {
|
|
|
+ if (timeDiff == null) {
|
|
|
+ timeDiff = DEFAULT_CACHE_TIMEOUT;
|
|
|
+ }
|
|
|
+ cacheMap.put(key, value);
|
|
|
+ timeoutMap.put(key, new Long[]{System.currentTimeMillis(), timeDiff});
|
|
|
+ cleanTimeoutExecutor.execute(() -> {
|
|
|
+ try {
|
|
|
+ cleanTimeoutCache();
|
|
|
+ Thread.sleep(10 * 1000);
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ log.error(e.getMessage(), e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据缓存key获取缓存对象
|
|
|
+ * @param key key
|
|
|
+ * @param refreshCache 是否刷新命中的缓存的存活时间 true是,false否
|
|
|
+ * @return 缓存对象
|
|
|
+ */
|
|
|
+ public Optional<Object> getCache(Object key, boolean refreshCache) {
|
|
|
+ Long[] cacheTime = timeoutMap.get(key);
|
|
|
+ if (cacheTime == null || cacheTime.length != CACHE_TIME_LENGTH) {
|
|
|
+ return Optional.empty();
|
|
|
+ }
|
|
|
+ if (cacheTime[0] + cacheTime[1] < System.currentTimeMillis()) {
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ cacheMap.remove(key);
|
|
|
+ return Optional.empty();
|
|
|
+ }
|
|
|
+ Object value = cacheMap.get(key);
|
|
|
+ if (value == null) {
|
|
|
+ cacheMap.remove(key);
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ } else if (refreshCache) {
|
|
|
+ cacheTime[0] = System.currentTimeMillis();
|
|
|
+ timeoutMap.put(key, cacheTime);
|
|
|
+ }
|
|
|
+ return Optional.ofNullable(value);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据缓存key删除缓存对象
|
|
|
+ * @param key key
|
|
|
+ */
|
|
|
+ public void removeCache(Object key) {
|
|
|
+ timeoutMap.remove(key);
|
|
|
+ cacheMap.remove(key);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取缓存实例
|
|
|
+ * @return cache
|
|
|
+ */
|
|
|
+ public static CommonCache getInstance() {
|
|
|
+ return SingleInstance.INSTANCE;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 静态内部类
|
|
|
+ */
|
|
|
+ private static class SingleInstance {
|
|
|
+ /**
|
|
|
+ * 单例
|
|
|
+ */
|
|
|
+ private static final CommonCache INSTANCE= new CommonCache();
|
|
|
+ }
|
|
|
+}
|