🎈 perf: 优化 Redis 策略

This commit is contained in:
imsyy
2024-12-07 10:11:15 +08:00
parent 489723985b
commit a05578f0f4
3 changed files with 44 additions and 43 deletions

View File

@@ -43,6 +43,7 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"dotenv": "^16.4.6", "dotenv": "^16.4.6",
"feed": "^4.2.2", "feed": "^4.2.2",
"flatted": "^3.3.2",
"hono": "^4.6.12", "hono": "^4.6.12",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"ioredis": "^5.4.1", "ioredis": "^5.4.1",
@@ -50,7 +51,6 @@
"node-cache": "^5.1.2", "node-cache": "^5.1.2",
"rss-parser": "^3.13.0", "rss-parser": "^3.13.0",
"user-agents": "^1.1.379", "user-agents": "^1.1.379",
"uuid": "^11.0.3",
"winston": "^3.17.0" "winston": "^3.17.0"
}, },
"devDependencies": { "devDependencies": {

12
pnpm-lock.yaml generated
View File

@@ -29,6 +29,9 @@ importers:
feed: feed:
specifier: ^4.2.2 specifier: ^4.2.2
version: 4.2.2 version: 4.2.2
flatted:
specifier: ^3.3.2
version: 3.3.2
hono: hono:
specifier: ^4.6.12 specifier: ^4.6.12
version: 4.6.12 version: 4.6.12
@@ -50,9 +53,6 @@ importers:
user-agents: user-agents:
specifier: ^1.1.379 specifier: ^1.1.379
version: 1.1.379 version: 1.1.379
uuid:
specifier: ^11.0.3
version: 11.0.3
winston: winston:
specifier: ^3.17.0 specifier: ^3.17.0
version: 3.17.0 version: 3.17.0
@@ -1055,10 +1055,6 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
uuid@11.0.3:
resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==}
hasBin: true
whatwg-encoding@3.1.1: whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@@ -2008,8 +2004,6 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
uuid@11.0.3: {}
whatwg-encoding@3.1.1: whatwg-encoding@3.1.1:
dependencies: dependencies:
iconv-lite: 0.6.3 iconv-lite: 0.6.3

View File

@@ -1,4 +1,5 @@
import { config } from "../config.js"; import { config } from "../config.js";
import { stringify, parse } from "flatted";
import logger from "./logger.js"; import logger from "./logger.js";
import NodeCache from "node-cache"; import NodeCache from "node-cache";
import Redis from "ioredis"; import Redis from "ioredis";
@@ -25,6 +26,9 @@ const redis = new Redis({
host: config.REDIS_HOST, host: config.REDIS_HOST,
port: config.REDIS_PORT, port: config.REDIS_PORT,
password: config.REDIS_PASSWORD, password: config.REDIS_PASSWORD,
maxRetriesPerRequest: 5,
// 重试策略:最小延迟 50ms最大延迟 2s
retryStrategy: (times) => Math.min(times * 50, 2000),
// 仅在第一次建立连接 // 仅在第一次建立连接
lazyConnect: true, lazyConnect: true,
}); });
@@ -33,7 +37,24 @@ const redis = new Redis({
let isRedisAvailable: boolean = false; let isRedisAvailable: boolean = false;
let isRedisTried: 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) => { redis.on("error", (err) => {
if (!isRedisTried) { if (!isRedisTried) {
isRedisAvailable = false; isRedisAvailable = false;
@@ -44,23 +65,14 @@ redis.on("error", (err) => {
} }
}); });
// Redis 连接状态 // NodeCache 事件监听
const ensureRedisConnection = async () => { cache.on("expired", (key) => {
if (!isRedisTried) { logger.info(`⏳ [NodeCache] Key "${key}" has expired.`);
try { });
await redis.connect();
isRedisAvailable = true; cache.on("del", (key) => {
isRedisTried = true; logger.info(`🗑️ [NodeCache] Key "${key}" has been deleted.`);
logger.info("📦 [Redis] connected successfully."); });
} catch (error) {
isRedisAvailable = false;
isRedisTried = true;
logger.error(
`📦 [Redis] connection failed: ${error instanceof Error ? error.message : "Unknown error"}`,
);
}
}
};
/** /**
* 从缓存中获取数据 * 从缓存中获取数据
@@ -72,10 +84,7 @@ export const getCache = async (key: string): Promise<CacheData | undefined> => {
if (isRedisAvailable) { if (isRedisAvailable) {
try { try {
const redisResult = await redis.get(key); const redisResult = await redis.get(key);
if (redisResult) { if (redisResult) return parse(redisResult);
const data = JSON.parse(redisResult);
return data;
}
} catch (error) { } catch (error) {
logger.error( logger.error(
`📦 [Redis] get error: ${error instanceof Error ? error.message : "Unknown error"}`, `📦 [Redis] get error: ${error instanceof Error ? error.message : "Unknown error"}`,
@@ -100,7 +109,7 @@ export const setCache = async (
// 尝试写入 Redis // 尝试写入 Redis
if (isRedisAvailable && !Buffer.isBuffer(value?.data)) { if (isRedisAvailable && !Buffer.isBuffer(value?.data)) {
try { 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`); if (logger) logger.info(`💾 [REDIS] ${key} has been cached`);
} catch (error) { } catch (error) {
logger.error( logger.error(
@@ -120,16 +129,14 @@ export const setCache = async (
*/ */
export const delCache = async (key: string): Promise<boolean> => { export const delCache = async (key: string): Promise<boolean> => {
let redisSuccess = true; let redisSuccess = true;
if (isRedisAvailable) { try {
try { await redis.del(key);
await redis.del(key); logger.info(`🗑️ [REDIS] ${key} has been deleted from Redis`);
if (logger) logger.info(`🗑️ [REDIS] ${key} has been deleted from Redis`); } catch (error) {
} catch (error) { redisSuccess = false;
logger.error( logger.error(
`📦 [Redis] del error: ${error instanceof Error ? error.message : "Unknown error"}`, `📦 [Redis] del error: ${error instanceof Error ? error.message : "Unknown error"}`,
); );
redisSuccess = false;
}
} }
// 尝试删除 NodeCache // 尝试删除 NodeCache
const nodeCacheSuccess = cache.del(key) > 0; const nodeCacheSuccess = cache.del(key) > 0;