UNPKG

yijing-bazi-mcp

Version:

易经八字分析 MCP 服务器 - 提供传统命理分析和易经卦象解读的AI工具

748 lines (653 loc) 16.5 kB
/** * 缓存管理工具类 * 提供内存缓存、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 };