diff --git a/src/main/java/im/zhaojun/zfile/cache/DriveCacheKey.java b/src/main/java/im/zhaojun/zfile/cache/DriveCacheKey.java new file mode 100644 index 0000000..31a4ba6 --- /dev/null +++ b/src/main/java/im/zhaojun/zfile/cache/DriveCacheKey.java @@ -0,0 +1,19 @@ +package im.zhaojun.zfile.cache; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhaojun + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DriveCacheKey { + + private Integer driveId; + + private String key; + +} diff --git a/src/main/java/im/zhaojun/zfile/cache/MyTimedCache.java b/src/main/java/im/zhaojun/zfile/cache/MyTimedCache.java new file mode 100644 index 0000000..48787b2 --- /dev/null +++ b/src/main/java/im/zhaojun/zfile/cache/MyTimedCache.java @@ -0,0 +1,43 @@ +package im.zhaojun.zfile.cache; + +import cn.hutool.cache.impl.CacheObj; +import cn.hutool.cache.impl.TimedCache; +import im.zhaojun.zfile.context.DriveContext; +import im.zhaojun.zfile.service.base.AbstractBaseFileService; +import im.zhaojun.zfile.util.SpringContextHolder; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +/** + * @author zhaojun + */ +@Slf4j +public class MyTimedCache extends TimedCache { + + private DriveContext driveContext; + + public MyTimedCache(long timeout) { + super(timeout); + } + + public MyTimedCache(long timeout, Map> map) { + super(timeout, map); + } + + @Override + protected void onRemove(K key, V cachedObject) { + log.debug("尝试刷新缓存: " + key); + if (driveContext == null) { + driveContext = SpringContextHolder.getBean(DriveContext.class); + } + DriveCacheKey cacheKey = (DriveCacheKey) key; + AbstractBaseFileService baseFileService = driveContext.getDriveService(cacheKey.getDriveId()); + try { + baseFileService.fileList(cacheKey.getKey()); + } catch (Exception e) { + log.error("尝试刷新驱动器 {} 的 {} 失败, ", cacheKey.getDriveId(), cacheKey.getKey()); + e.printStackTrace(); + } + } +} diff --git a/src/main/java/im/zhaojun/zfile/cache/ZFileCache.java b/src/main/java/im/zhaojun/zfile/cache/ZFileCache.java index 0fe2819..50fb066 100644 --- a/src/main/java/im/zhaojun/zfile/cache/ZFileCache.java +++ b/src/main/java/im/zhaojun/zfile/cache/ZFileCache.java @@ -1,8 +1,6 @@ package im.zhaojun.zfile.cache; -import cn.hutool.cache.CacheUtil; import cn.hutool.cache.impl.CacheObj; -import cn.hutool.cache.impl.TimedCache; import cn.hutool.core.util.StrUtil; import im.zhaojun.zfile.model.constant.ZFileConstant; import im.zhaojun.zfile.model.dto.FileItemDTO; @@ -33,6 +31,9 @@ public class ZFileCache { @Value("${zfile.cache.timeout}") private long timeout; + @Value("${zfile.cache.auto-refresh.interval}") + private long autoRefreshInterval; + /** * 缓存 map 对象. @@ -44,7 +45,7 @@ public class ZFileCache { * key: 文件夹路径 * value: 文件夹中内容 */ - private ConcurrentMap>> drivesCache = new ConcurrentHashMap<>(); + private ConcurrentMap>> drivesCache = new ConcurrentHashMap<>(); /** * 系统设置缓存 @@ -68,7 +69,7 @@ public class ZFileCache { * 文件夹中列表 */ public synchronized void put(Integer driveId, String key, List value) { - getCacheByDriveId(driveId).put(key, value); + getCacheByDriveId(driveId).put(new DriveCacheKey(driveId, key), value); } @@ -84,7 +85,7 @@ public class ZFileCache { * @return 驱动器中文件夹的内容 */ public List get(Integer driveId, String key) { - return getCacheByDriveId(driveId).get(key, false); + return getCacheByDriveId(driveId).get(new DriveCacheKey(driveId, key), false); } @@ -161,10 +162,10 @@ public class ZFileCache { * @return 所有缓存 key */ public Set keySet(Integer driveId) { - Iterator>> cacheObjIterator = getCacheByDriveId(driveId).cacheObjIterator(); + Iterator>> cacheObjIterator = getCacheByDriveId(driveId).cacheObjIterator(); Set keys = new HashSet<>(); while (cacheObjIterator.hasNext()) { - keys.add(cacheObjIterator.next().getKey()); + keys.add(cacheObjIterator.next().getKey().getKey()); } return keys; } @@ -180,7 +181,7 @@ public class ZFileCache { * 文件夹路径 */ public void remove(Integer driveId, String key) { - getCacheByDriveId(driveId).remove(key); + getCacheByDriveId(driveId).remove(new DriveCacheKey(driveId, key)); } @@ -241,14 +242,74 @@ public class ZFileCache { * * @return 驱动器对应的缓存 */ - private synchronized TimedCache> getCacheByDriveId(Integer driveId) { - TimedCache> driveCache = drivesCache.get(driveId); + private synchronized MyTimedCache> getCacheByDriveId(Integer driveId) { + MyTimedCache> driveCache = drivesCache.get(driveId); if (driveCache == null) { - driveCache = CacheUtil.newTimedCache(timeout * 1000); + driveCache = new MyTimedCache<>(timeout * 1000); drivesCache.put(driveId, driveCache); + startAutoCacheRefresh(driveId); } - return driveCache; } + + /** + * 获取指定驱动器的缓存命中数 + * + * @param driveId + * 驱动器 ID + * + * @return 缓存命中数 + */ + public int getHitCount(Integer driveId) { + return getCacheByDriveId(driveId).getHitCount(); + } + + + /** + * 获取指定驱动器的缓存未命中数 + * + * @param driveId + * 驱动器 ID + * + * @return 缓存未命中数 + */ + public int getMissCount(Integer driveId) { + return getCacheByDriveId(driveId).getMissCount(); + } + + + /** + * 开启缓存自动刷新, 仅当数据库设置为开启时, 才会真正开启缓存自动刷新. + * + * @param driveId + * 驱动器 ID + */ + public void startAutoCacheRefresh(Integer driveId) { + DriveConfig driveConfig = driverConfigRepository.findById(driveId).get(); + Boolean autoRefreshCache = driveConfig.getAutoRefreshCache(); + if (autoRefreshCache != null && autoRefreshCache) { + MyTimedCache> driveCache = drivesCache.get(driveId); + if (driveCache == null) { + driveCache = new MyTimedCache<>(timeout * 1000); + drivesCache.put(driveId, driveCache); + } + driveCache.schedulePrune(autoRefreshInterval * 1000); + } + } + + + /** + * 停止缓存自动刷新 + * + * @param driveId + * 驱动器 ID + */ + public void stopAutoCacheRefresh(Integer driveId) { + MyTimedCache> driveCache = drivesCache.get(driveId); + if (driveCache != null) { + driveCache.cancelPruneSchedule(); + } + } + } \ No newline at end of file diff --git a/src/main/java/im/zhaojun/zfile/controller/admin/CacheController.java b/src/main/java/im/zhaojun/zfile/controller/admin/CacheController.java index 3935b26..cd683bb 100644 --- a/src/main/java/im/zhaojun/zfile/controller/admin/CacheController.java +++ b/src/main/java/im/zhaojun/zfile/controller/admin/CacheController.java @@ -1,7 +1,9 @@ package im.zhaojun.zfile.controller.admin; +import im.zhaojun.zfile.model.dto.CacheInfoDTO; import im.zhaojun.zfile.model.dto.ResultBean; import im.zhaojun.zfile.service.DriveConfigService; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -21,17 +23,50 @@ public class CacheController { @Resource private DriveConfigService driveConfigService; - @PostMapping("/{driveId}/enable") public ResultBean enableCache(@PathVariable("driveId") Integer driveId) { driveConfigService.updateCacheStatus(driveId, true); return ResultBean.success(); } + @PostMapping("/{driveId}/disable") public ResultBean disableCache(@PathVariable("driveId") Integer driveId) { driveConfigService.updateCacheStatus(driveId, false); return ResultBean.success(); } + + @GetMapping("/{driveId}/info") + public ResultBean cacheInfo(@PathVariable("driveId") Integer driveId) { + CacheInfoDTO cacheInfo = driveConfigService.findCacheInfo(driveId); + return ResultBean.success(cacheInfo); + } + + + @PostMapping("/{driveId}/refresh") + public ResultBean refreshCache(@PathVariable("driveId") Integer driveId, String key) throws Exception { + driveConfigService.refreshCache(driveId, key); + return ResultBean.success(); + } + + @PostMapping("/{driveId}/auto-refresh/start") + public ResultBean enableAutoRefresh(@PathVariable("driveId") Integer driveId) { + driveConfigService.startAutoCacheRefresh(driveId); + return ResultBean.success(); + } + + + @PostMapping("/{driveId}/auto-refresh/stop") + public ResultBean disableAutoRefresh(@PathVariable("driveId") Integer driveId) { + driveConfigService.stopAutoCacheRefresh(driveId); + return ResultBean.success(); + } + + @PostMapping("/{driveId}/clear") + public ResultBean clearCache(@PathVariable("driveId") Integer driveId) { + driveConfigService.clearCache(driveId); + return ResultBean.success(); + } + } \ No newline at end of file diff --git a/src/main/java/im/zhaojun/zfile/model/dto/CacheInfoDTO.java b/src/main/java/im/zhaojun/zfile/model/dto/CacheInfoDTO.java new file mode 100644 index 0000000..437c956 --- /dev/null +++ b/src/main/java/im/zhaojun/zfile/model/dto/CacheInfoDTO.java @@ -0,0 +1,23 @@ +package im.zhaojun.zfile.model.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.util.Set; + +/** + * @author zhaojun + */ +@Data +@AllArgsConstructor +public class CacheInfoDTO { + + private Integer cacheCount; + + private Integer hitCount; + + private Integer missCount; + + private Set cacheKeys; + +} diff --git a/src/main/java/im/zhaojun/zfile/service/DriveConfigService.java b/src/main/java/im/zhaojun/zfile/service/DriveConfigService.java index 44bb99c..69676b8 100644 --- a/src/main/java/im/zhaojun/zfile/service/DriveConfigService.java +++ b/src/main/java/im/zhaojun/zfile/service/DriveConfigService.java @@ -1,8 +1,11 @@ package im.zhaojun.zfile.service; +import im.zhaojun.zfile.cache.ZFileCache; +import im.zhaojun.zfile.context.DriveContext; import im.zhaojun.zfile.context.StorageTypeContext; import im.zhaojun.zfile.exception.InitializeException; import im.zhaojun.zfile.model.constant.StorageConfigConstant; +import im.zhaojun.zfile.model.dto.CacheInfoDTO; import im.zhaojun.zfile.model.dto.DriveConfigDTO; import im.zhaojun.zfile.model.dto.StorageStrategyConfig; import im.zhaojun.zfile.model.entity.DriveConfig; @@ -11,7 +14,6 @@ import im.zhaojun.zfile.model.enums.StorageTypeEnum; import im.zhaojun.zfile.repository.DriverConfigRepository; import im.zhaojun.zfile.repository.StorageConfigRepository; import im.zhaojun.zfile.service.base.AbstractBaseFileService; -import im.zhaojun.zfile.context.DriveContext; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; @@ -21,6 +23,7 @@ import javax.annotation.Resource; import java.lang.reflect.Field; import java.util.List; import java.util.Objects; +import java.util.Set; /** * 驱动器 Service 类 @@ -39,6 +42,9 @@ public class DriveConfigService { @Resource private DriveContext driveContext; + @Resource + private ZFileCache zFileCache; + public static final Class STORAGE_STRATEGY_CONFIG_CLASS = StorageStrategyConfig.class; /** @@ -60,7 +66,7 @@ public class DriveConfigService { * @return 驱动器设置 */ public DriveConfig findById(Integer id) { - return driverConfigRepository.getOne(id); + return driverConfigRepository.findById(id).get(); } @@ -137,6 +143,12 @@ public class DriveConfigService { BeanUtils.copyProperties(driveConfigDTO, driveConfig); driverConfigRepository.save(driveConfig); + if (driveConfig.getAutoRefreshCache()) { + startAutoCacheRefresh(driveConfig.getId()); + } else { + stopAutoCacheRefresh(driveConfig.getId()); + } + // 保存存储策略设置. StorageStrategyConfig storageStrategyConfig = driveConfigDTO.getStorageStrategyConfig(); @@ -221,4 +233,97 @@ public class DriveConfigService { driverConfigRepository.save(driveConfig); } } + + + /** + * 更新指定驱动器的缓存启用状态 + * + * @param driveId + * 驱动器 ID + * + * @param autoRefreshCache + * 是否启用缓存自动刷新 + */ + public void updateAutoRefreshCacheStatus(Integer driveId, Boolean autoRefreshCache) { + DriveConfig driveConfig = findById(driveId); + if (driveConfig != null) { + driveConfig.setAutoRefreshCache(autoRefreshCache); + driverConfigRepository.save(driveConfig); + } + } + + + /** + * 获取指定驱动器的缓存信息 + * @param driveId + * 驱动器 ID + * @return 缓存信息 + */ + public CacheInfoDTO findCacheInfo(Integer driveId) { + int hitCount = zFileCache.getHitCount(driveId); + int missCount = zFileCache.getMissCount(driveId); + Set keys = zFileCache.keySet(driveId); + int cacheCount = keys.size(); + return new CacheInfoDTO(cacheCount, hitCount, missCount, keys); + } + + + + /** + * 刷新指定 key 的缓存: + * 1. 清空此 key 的缓存. + * 2. 重新调用方法写入缓存. + * + * @param driveId + * 驱动器 ID + * + * @param key + * 缓存 key (文件夹名称) + */ + public void refreshCache(Integer driveId, String key) throws Exception { + zFileCache.remove(driveId, key); + AbstractBaseFileService baseFileService = driveContext.getDriveService(driveId); + baseFileService.fileList(key); + } + + + + /** + * 开启缓存自动刷新, 仅当数据库设置为开启时, 才会真正开启缓存自动刷新. + * + * @param driveId + * 驱动器 ID + */ + public void startAutoCacheRefresh(Integer driveId) { + DriveConfig driveConfig = findById(driveId); + driveConfig.setAutoRefreshCache(true); + driverConfigRepository.save(driveConfig); + zFileCache.startAutoCacheRefresh(driveId); + } + + + /** + * 停止缓存自动刷新 + * + * @param driveId + * 驱动器 ID + */ + public void stopAutoCacheRefresh(Integer driveId) { + DriveConfig driveConfig = findById(driveId); + driveConfig.setAutoRefreshCache(false); + driverConfigRepository.save(driveConfig); + zFileCache.stopAutoCacheRefresh(driveId); + } + + /** + * 清理缓存 + * + * @param driveId + * 驱动器 ID + */ + public void clearCache(Integer driveId) { + zFileCache.clear(driveId); + } + + } \ No newline at end of file diff --git a/src/main/java/im/zhaojun/zfile/service/base/AbstractBaseFileService.java b/src/main/java/im/zhaojun/zfile/service/base/AbstractBaseFileService.java index 38ebd81..88bca56 100644 --- a/src/main/java/im/zhaojun/zfile/service/base/AbstractBaseFileService.java +++ b/src/main/java/im/zhaojun/zfile/service/base/AbstractBaseFileService.java @@ -140,21 +140,6 @@ public abstract class AbstractBaseFileService implements BaseFileService { } - /** - * 刷新指定 key 的缓存: - * 1. 清空此 key 的缓存. - * 2. 重新调用方法写入缓存. - * - * @param key - * 缓存 key (文件夹名称) - */ - public void refreshCache(String key) throws Exception { - zFileCache.remove(driveId, key); - BaseFileService currentFileService = (BaseFileService) AopContext.currentProxy(); - currentFileService.fileList(key); - } - - /** * 获取单个文件信息 * diff --git a/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/src/main/resources/META-INF/additional-spring-configuration-metadata.json index d058d39..d6ddbf4 100644 --- a/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -5,20 +5,10 @@ "type": "java.lang.Long", "description": "目录缓存过期时间 和 下载地址过期时间. 单位为秒." }, - { - "name": "zfile.cache.auto-refresh.enable", - "type": "java.lang.Boolean", - "description": "是否开启自动刷新缓存." - }, - { - "name": "zfile.cache.auto-refresh.delay", - "type": "java.lang.Long", - "description": "启动项目后多久开始自动刷新缓存, 推荐与 interval 一致, 因为项目启动时会缓存所有文件. 单位为秒.." - }, { "name": "zfile.cache.auto-refresh.interval", "type": "java.lang.Long", - "description": "任务间隔时间, 也就是每多长时间会自动刷新缓存一次.." + "description": "任务间隔时间, 每隔多长时间检测一次到期的缓存 KEY 并自动刷新, 单位为秒." }, { "name": "zfile.constant.readme", diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b06e456..fc6c998 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -51,10 +51,8 @@ spring: zfile: cache: -# auto-refresh: # 新版本尚未实现此功能 -# enable: true # 是否开启自动刷新缓存. -# delay: 1800 # 启动项目后多久开始自动刷新缓存, 推荐与 interval 一致, 因为项目启动时会缓存所有文件. -# interval: 1800 # 任务间隔时间, 也就是每多长时间会自动刷新缓存一次. + auto-refresh: + interval: 1 timeout: 1800 constant: readme: readme.md