/**
* 缓存管理工具类
* 提供内存缓存、LRU缓存和持久化缓存功能
*/
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const Logger = require('./logger');
const config = require('../config/config');
const { ErrorHandler } = require('./error-handler');
/**
* LRU缓存节点
*/
class CacheNode {
constructor(key, value, ttl = 0) {
this.key = key;
this.value = value;
this.ttl = ttl;
this.createdAt = Date.now();
this.accessedAt = Date.now();
this.accessCount = 1;
this.prev = null;
this.next = null;
}
/**
* 检查是否过期
* @returns {boolean} 是否过期
*/
isExpired() {
if (this.ttl <= 0) return false;
return Date.now() - this.createdAt > this.ttl * 1000;
}
/**
* 更新访问信息
*/
updateAccess() {
this.accessedAt = Date.now();
this.accessCount++;
}
/**
* 获取节点信息
* @returns {Object} 节点信息
*/
getInfo() {
return {
key: this.key,
size: this.getSize(),
ttl: this.ttl,
createdAt: this.createdAt,
accessedAt: this.accessedAt,
accessCount: this.accessCount,
age: Date.now() - this.createdAt,
isExpired: this.isExpired()
};
}
/**
* 获取值的大小(字节)
* @returns {number} 大小
*/
getSize() {
return Buffer.byteLength(JSON.stringify(this.value), 'utf8');
}
}
/**
* LRU缓存实现
*/
class LRUCache {
constructor(maxSize = 1000, defaultTTL = 3600) {
this.maxSize = maxSize;
this.defaultTTL = defaultTTL;
this.cache = new Map();
this.head = new CacheNode('head', null);
this.tail = new CacheNode('tail', null);
this.head.next = this.tail;
this.tail.prev = this.head;
this.logger = new Logger();
this.errorHandler = new ErrorHandler();
// 统计信息
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
errors: 0
};
// 定期清理过期项
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, (config.get('performance.cache.checkPeriod') || 600) * 1000);
}
/**
* 获取缓存值
* @param {string} key - 缓存键
* @returns {*} 缓存值
*/
get(key) {
try {
const node = this.cache.get(key);
if (!node) {
this.stats.misses++;
return null;
}
if (node.isExpired()) {
this.delete(key);
this.stats.misses++;
return null;
}
// 更新访问信息并移到头部
node.updateAccess();
this.moveToHead(node);
this.stats.hits++;
return node.value;
} catch (error) {
this.stats.errors++;
this.logger.error('缓存获取错误', { key, error: error.message });
return null;
}
}
/**
* 设置缓存值
* @param {string} key - 缓存键
* @param {*} value - 缓存值
* @param {number} ttl - 生存时间(秒)
* @returns {boolean} 是否成功
*/
set(key, value, ttl = this.defaultTTL) {
try {
// 检查是否已存在
if (this.cache.has(key)) {
const node = this.cache.get(key);
node.value = value;
node.ttl = ttl;
node.createdAt = Date.now();
node.updateAccess();
this.moveToHead(node);
} else {
// 创建新节点
const node = new CacheNode(key, value, ttl);
// 检查容量
if (this.cache.size >= this.maxSize) {
this.evictLRU();
}
// 添加到缓存
this.cache.set(key, node);
this.addToHead(node);
}
this.stats.sets++;
return true;
} catch (error) {
this.stats.errors++;
this.logger.error('缓存设置错误', { key, error: error.message });
return false;
}
}
/**
* 删除缓存值
* @param {string} key - 缓存键
* @returns {boolean} 是否成功
*/
delete(key) {
try {
const node = this.cache.get(key);
if (node) {
this.cache.delete(key);
this.removeNode(node);
this.stats.deletes++;
return true;
}
return false;
} catch (error) {
this.stats.errors++;
this.logger.error('缓存删除错误', { key, error: error.message });
return false;
}
}
/**
* 检查缓存是否存在
* @param {string} key - 缓存键
* @returns {boolean} 是否存在
*/
has(key) {
const node = this.cache.get(key);
return node && !node.isExpired();
}
/**
* 清空缓存
*/
clear() {
this.cache.clear();
this.head.next = this.tail;
this.tail.prev = this.head;
this.resetStats();
}
/**
* 获取缓存大小
* @returns {number} 缓存项数量
*/
size() {
return this.cache.size;
}
/**
* 获取所有缓存键
* @returns {Array<string>} 缓存键数组
*/
keys() {
return Array.from(this.cache.keys());
}
/**
* 获取缓存统计信息
* @returns {Object} 统计信息
*/
getStats() {
const hitRate = this.stats.hits + this.stats.misses > 0
? (this.stats.hits / (this.stats.hits + this.stats.misses) * 100).toFixed(2)
: 0;
return {
...this.stats,
hitRate: `${hitRate}%`,
size: this.cache.size,
maxSize: this.maxSize,
memoryUsage: this.getMemoryUsage()
};
}
/**
* 获取内存使用情况
* @returns {Object} 内存使用信息
*/
getMemoryUsage() {
let totalSize = 0;
let expiredCount = 0;
for (const node of this.cache.values()) {
totalSize += node.getSize();
if (node.isExpired()) {
expiredCount++;
}
}
return {
totalSize,
averageSize: this.cache.size > 0 ? Math.round(totalSize / this.cache.size) : 0,
expiredCount
};
}
/**
* 清理过期项
* @returns {number} 清理的项数
*/
cleanup() {
let cleanedCount = 0;
const expiredKeys = [];
for (const [key, node] of this.cache.entries()) {
if (node.isExpired()) {
expiredKeys.push(key);
}
}
for (const key of expiredKeys) {
this.delete(key);
cleanedCount++;
}
if (cleanedCount > 0) {
this.logger.debug('缓存清理完成', { cleanedCount });
}
return cleanedCount;
}
/**
* 重置统计信息
*/
resetStats() {
this.stats = {
hits: 0,
misses: 0,
sets: 0,
deletes: 0,
evictions: 0,
errors: 0
};
}
/**
* 移动节点到头部
* @param {CacheNode} node - 缓存节点
*/
moveToHead(node) {
this.removeNode(node);
this.addToHead(node);
}
/**
* 添加节点到头部
* @param {CacheNode} node - 缓存节点
*/
addToHead(node) {
node.prev = this.head;
node.next = this.head.next;
this.head.next.prev = node;
this.head.next = node;
}
/**
* 移除节点
* @param {CacheNode} node - 缓存节点
*/
removeNode(node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
/**
* 淘汰最少使用的节点
*/
evictLRU() {
const tail = this.tail.prev;
if (tail !== this.head) {
this.cache.delete(tail.key);
this.removeNode(tail);
this.stats.evictions++;
}
}
/**
* 销毁缓存
*/
destroy() {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
}
this.clear();
}
}
/**
* 持久化缓存管理器
*/
class PersistentCache {
constructor(cacheDir = 'cache') {
this.cacheDir = path.resolve(cacheDir);
this.logger = new Logger();
this.errorHandler = new ErrorHandler();
this.ensureCacheDir();
}
/**
* 确保缓存目录存在
*/
ensureCacheDir() {
if (!fs.existsSync(this.cacheDir)) {
fs.mkdirSync(this.cacheDir, { recursive: true });
}
}
/**
* 生成缓存文件路径
* @param {string} key - 缓存键
* @returns {string} 文件路径
*/
getCacheFilePath(key) {
const hash = crypto.createHash('md5').update(key).digest('hex');
return path.join(this.cacheDir, `${hash}.json`);
}
/**
* 获取持久化缓存
* @param {string} key - 缓存键
* @returns {*} 缓存值
*/
async get(key) {
try {
const filePath = this.getCacheFilePath(key);
if (!fs.existsSync(filePath)) {
return null;
}
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// 检查是否过期
if (data.ttl > 0 && Date.now() - data.createdAt > data.ttl * 1000) {
await this.delete(key);
return null;
}
return data.value;
} catch (error) {
this.logger.error('持久化缓存获取错误', { key, error: error.message });
return null;
}
}
/**
* 设置持久化缓存
* @param {string} key - 缓存键
* @param {*} value - 缓存值
* @param {number} ttl - 生存时间(秒)
* @returns {boolean} 是否成功
*/
async set(key, value, ttl = 0) {
try {
const filePath = this.getCacheFilePath(key);
const data = {
key,
value,
ttl,
createdAt: Date.now()
};
fs.writeFileSync(filePath, JSON.stringify(data), 'utf8');
return true;
} catch (error) {
this.logger.error('持久化缓存设置错误', { key, error: error.message });
return false;
}
}
/**
* 删除持久化缓存
* @param {string} key - 缓存键
* @returns {boolean} 是否成功
*/
async delete(key) {
try {
const filePath = this.getCacheFilePath(key);
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
return true;
}
return false;
} catch (error) {
this.logger.error('持久化缓存删除错误', { key, error: error.message });
return false;
}
}
/**
* 清理过期的持久化缓存
* @returns {number} 清理的文件数
*/
async cleanup() {
try {
const files = fs.readdirSync(this.cacheDir);
let cleanedCount = 0;
for (const file of files) {
if (!file.endsWith('.json')) continue;
const filePath = path.join(this.cacheDir, file);
try {
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (data.ttl > 0 && Date.now() - data.createdAt > data.ttl * 1000) {
fs.unlinkSync(filePath);
cleanedCount++;
}
} catch (error) {
// 删除损坏的缓存文件
fs.unlinkSync(filePath);
cleanedCount++;
}
}
if (cleanedCount > 0) {
this.logger.debug('持久化缓存清理完成', { cleanedCount });
}
return cleanedCount;
} catch (error) {
this.logger.error('持久化缓存清理错误', { error: error.message });
return 0;
}
}
/**
* 获取缓存统计信息
* @returns {Object} 统计信息
*/
getStats() {
try {
const files = fs.readdirSync(this.cacheDir);
const jsonFiles = files.filter(f => f.endsWith('.json'));
let totalSize = 0;
let expiredCount = 0;
for (const file of jsonFiles) {
const filePath = path.join(this.cacheDir, file);
const stats = fs.statSync(filePath);
totalSize += stats.size;
try {
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
if (data.ttl > 0 && Date.now() - data.createdAt > data.ttl * 1000) {
expiredCount++;
}
} catch (error) {
expiredCount++; // 损坏的文件也算过期
}
}
return {
fileCount: jsonFiles.length,
totalSize,
averageSize: jsonFiles.length > 0 ? Math.round(totalSize / jsonFiles.length) : 0,
expiredCount,
cacheDir: this.cacheDir
};
} catch (error) {
this.logger.error('获取持久化缓存统计错误', { error: error.message });
return {
fileCount: 0,
totalSize: 0,
averageSize: 0,
expiredCount: 0,
cacheDir: this.cacheDir
};
}
}
}
/**
* 多层缓存管理器
*/
class CacheManager {
constructor(options = {}) {
const cacheConfig = config.get('performance.cache') || {};
this.options = {
enableMemoryCache: options.enableMemoryCache !== false,
enablePersistentCache: options.enablePersistentCache !== false,
memoryMaxSize: options.memoryMaxSize || cacheConfig.maxSize || 1000,
defaultTTL: options.defaultTTL || cacheConfig.ttl || 3600,
cacheDir: options.cacheDir || path.join(process.cwd(), 'cache'),
...options
};
this.logger = new Logger();
this.errorHandler = new ErrorHandler();
// 初始化缓存层
if (this.options.enableMemoryCache) {
this.memoryCache = new LRUCache(this.options.memoryMaxSize, this.options.defaultTTL);
}
if (this.options.enablePersistentCache) {
this.persistentCache = new PersistentCache(this.options.cacheDir);
}
}
/**
* 获取缓存值
* @param {string} key - 缓存键
* @returns {Promise<*>} 缓存值
*/
async get(key) {
try {
// 先从内存缓存获取
if (this.memoryCache) {
const memoryValue = this.memoryCache.get(key);
if (memoryValue !== null) {
return memoryValue;
}
}
// 再从持久化缓存获取
if (this.persistentCache) {
const persistentValue = await this.persistentCache.get(key);
if (persistentValue !== null) {
// 回写到内存缓存
if (this.memoryCache) {
this.memoryCache.set(key, persistentValue);
}
return persistentValue;
}
}
return null;
} catch (error) {
this.logger.error('缓存获取错误', { key, error: error.message });
return null;
}
}
/**
* 设置缓存值
* @param {string} key - 缓存键
* @param {*} value - 缓存值
* @param {number} ttl - 生存时间(秒)
* @returns {Promise<boolean>} 是否成功
*/
async set(key, value, ttl = this.options.defaultTTL) {
try {
let success = true;
// 设置内存缓存
if (this.memoryCache) {
success = this.memoryCache.set(key, value, ttl) && success;
}
// 设置持久化缓存
if (this.persistentCache) {
success = await this.persistentCache.set(key, value, ttl) && success;
}
return success;
} catch (error) {
this.logger.error('缓存设置错误', { key, error: error.message });
return false;
}
}
/**
* 删除缓存值
* @param {string} key - 缓存键
* @returns {Promise<boolean>} 是否成功
*/
async delete(key) {
try {
let success = true;
// 删除内存缓存
if (this.memoryCache) {
success = this.memoryCache.delete(key) && success;
}
// 删除持久化缓存
if (this.persistentCache) {
success = await this.persistentCache.delete(key) && success;
}
return success;
} catch (error) {
this.logger.error('缓存删除错误', { key, error: error.message });
return false;
}
}
/**
* 检查缓存是否存在
* @param {string} key - 缓存键
* @returns {Promise<boolean>} 是否存在
*/
async has(key) {
if (this.memoryCache && this.memoryCache.has(key)) {
return true;
}
if (this.persistentCache) {
const value = await this.persistentCache.get(key);
return value !== null;
}
return false;
}
/**
* 清理过期缓存
* @returns {Promise<Object>} 清理结果
*/
async cleanup() {
const result = {
memory: 0,
persistent: 0
};
if (this.memoryCache) {
result.memory = this.memoryCache.cleanup();
}
if (this.persistentCache) {
result.persistent = await this.persistentCache.cleanup();
}
return result;
}
/**
* 获取缓存统计信息
* @returns {Promise<Object>} 统计信息
*/
async getStats() {
const stats = {
memory: null,
persistent: null
};
if (this.memoryCache) {
stats.memory = this.memoryCache.getStats();
}
if (this.persistentCache) {
stats.persistent = this.persistentCache.getStats();
}
return stats;
}
/**
* 销毁缓存管理器
*/
destroy() {
if (this.memoryCache) {
this.memoryCache.destroy();
}
}
}
module.exports = {
LRUCache,
PersistentCache,
CacheManager
};