diff --git a/package.json b/package.json index 7c70d23..06f84f1 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "dayjs": "^1.11.13", "dotenv": "^16.4.6", "feed": "^4.2.2", + "flatted": "^3.3.2", "hono": "^4.6.12", "iconv-lite": "^0.6.3", "ioredis": "^5.4.1", @@ -50,7 +51,6 @@ "node-cache": "^5.1.2", "rss-parser": "^3.13.0", "user-agents": "^1.1.379", - "uuid": "^11.0.3", "winston": "^3.17.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f0aea8c..61835b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: feed: specifier: ^4.2.2 version: 4.2.2 + flatted: + specifier: ^3.3.2 + version: 3.3.2 hono: specifier: ^4.6.12 version: 4.6.12 @@ -50,9 +53,6 @@ importers: user-agents: specifier: ^1.1.379 version: 1.1.379 - uuid: - specifier: ^11.0.3 - version: 11.0.3 winston: specifier: ^3.17.0 version: 3.17.0 @@ -1055,10 +1055,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@11.0.3: - resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==} - hasBin: true - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -2008,8 +2004,6 @@ snapshots: util-deprecate@1.0.2: {} - uuid@11.0.3: {} - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 diff --git a/src/utils/cache.ts b/src/utils/cache.ts index ecaa4c1..958cc87 100644 --- a/src/utils/cache.ts +++ b/src/utils/cache.ts @@ -1,4 +1,5 @@ import { config } from "../config.js"; +import { stringify, parse } from "flatted"; import logger from "./logger.js"; import NodeCache from "node-cache"; import Redis from "ioredis"; @@ -25,6 +26,9 @@ const redis = new Redis({ host: config.REDIS_HOST, port: config.REDIS_PORT, password: config.REDIS_PASSWORD, + maxRetriesPerRequest: 5, + // 重试策略:最小延迟 50ms,最大延迟 2s + retryStrategy: (times) => Math.min(times * 50, 2000), // 仅在第一次建立连接 lazyConnect: true, }); @@ -33,7 +37,24 @@ const redis = new Redis({ let isRedisAvailable: boolean = false; let isRedisTried: boolean = false; -// Redis 连接错误 +// Redis 连接状态 +const ensureRedisConnection = async () => { + if (isRedisTried) return; + try { + if (redis.status !== "ready" && redis.status !== "connecting") await redis.connect(); + isRedisAvailable = true; + isRedisTried = true; + logger.info("📦 [Redis] connected successfully."); + } catch (error) { + isRedisAvailable = false; + isRedisTried = true; + logger.error( + `📦 [Redis] connection failed: ${error instanceof Error ? error.message : "Unknown error"}`, + ); + } +}; + +// Redis 事件监听 redis.on("error", (err) => { if (!isRedisTried) { isRedisAvailable = false; @@ -44,23 +65,14 @@ redis.on("error", (err) => { } }); -// Redis 连接状态 -const ensureRedisConnection = async () => { - if (!isRedisTried) { - try { - await redis.connect(); - isRedisAvailable = true; - isRedisTried = true; - logger.info("📦 [Redis] connected successfully."); - } catch (error) { - isRedisAvailable = false; - isRedisTried = true; - logger.error( - `📦 [Redis] connection failed: ${error instanceof Error ? error.message : "Unknown error"}`, - ); - } - } -}; +// NodeCache 事件监听 +cache.on("expired", (key) => { + logger.info(`⏳ [NodeCache] Key "${key}" has expired.`); +}); + +cache.on("del", (key) => { + logger.info(`🗑️ [NodeCache] Key "${key}" has been deleted.`); +}); /** * 从缓存中获取数据 @@ -72,10 +84,7 @@ export const getCache = async (key: string): Promise => { if (isRedisAvailable) { try { const redisResult = await redis.get(key); - if (redisResult) { - const data = JSON.parse(redisResult); - return data; - } + if (redisResult) return parse(redisResult); } catch (error) { logger.error( `📦 [Redis] get error: ${error instanceof Error ? error.message : "Unknown error"}`, @@ -100,7 +109,7 @@ export const setCache = async ( // 尝试写入 Redis if (isRedisAvailable && !Buffer.isBuffer(value?.data)) { try { - await redis.set(key, JSON.stringify(value), "EX", ttl); + await redis.set(key, stringify(value), "EX", ttl); if (logger) logger.info(`💾 [REDIS] ${key} has been cached`); } catch (error) { logger.error( @@ -120,16 +129,14 @@ export const setCache = async ( */ export const delCache = async (key: string): Promise => { let redisSuccess = true; - if (isRedisAvailable) { - try { - await redis.del(key); - if (logger) logger.info(`🗑️ [REDIS] ${key} has been deleted from Redis`); - } catch (error) { - logger.error( - `📦 [Redis] del error: ${error instanceof Error ? error.message : "Unknown error"}`, - ); - redisSuccess = false; - } + try { + await redis.del(key); + logger.info(`🗑️ [REDIS] ${key} has been deleted from Redis`); + } catch (error) { + redisSuccess = false; + logger.error( + `📦 [Redis] del error: ${error instanceof Error ? error.message : "Unknown error"}`, + ); } // 尝试删除 NodeCache const nodeCacheSuccess = cache.del(key) > 0;