Files
wwjcloud-nest-v1/wwjcloud/src/core/cache/multiLevelCacheService.ts
万物街 2084711030 feat: 完成配置中心重构和命名规范优化
- 重构config层为配置中心架构,支持动态配置管理
- 统一core层命名规范(event-bus→event, circuit-breaker→breaker, domain-sdk→sdk)
- 修复数据库连接配置路径问题
- 实现配置中心完整功能:系统配置、动态配置、配置验证、统计
- 优化目录结构,为微服务架构做准备
- 修复TypeScript编译错误和依赖注入问题
2025-08-28 05:19:14 +08:00

251 lines
7.2 KiB
TypeScript

import { Injectable, Inject, Logger } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import type { Cache } from 'cache-manager';
import { Redis } from 'ioredis';
import { CacheService } from './cacheService';
export interface MultiLevelCacheOptions {
l1Ttl?: number; // L1 缓存时间(秒)
l2Ttl?: number; // L2 缓存时间(秒)
prefix?: string;
}
@Injectable()
export class MultiLevelCacheService {
private readonly logger = new Logger(MultiLevelCacheService.name);
private readonly appPrefix = 'wwjcloud'; // 使用固定前缀,避免硬编码
constructor(
@Inject(CACHE_MANAGER) private l1Cache: Cache, // 内存缓存
@Inject('REDIS_CLIENT') private l2Cache: Redis, // Redis 缓存
private cacheService: CacheService,
) {}
/**
* 获取缓存(多级)
*/
async get<T>(key: string, options?: MultiLevelCacheOptions): Promise<T | null> {
try {
const fullKey = this.buildKey(key, options?.prefix);
// L1: 内存缓存
const l1Value = await this.l1Cache.get<T>(fullKey);
if (l1Value !== undefined && l1Value !== null) {
this.logger.debug(`L1 cache hit: ${fullKey}`);
return l1Value as T;
}
// L2: Redis 缓存
const l2Value = await this.l2Cache.get(fullKey);
if (l2Value !== null && l2Value !== undefined) {
const parsedValue = JSON.parse(l2Value) as T;
// 回填到 L1 缓存
await this.l1Cache.set(fullKey, parsedValue, options?.l1Ttl || 60);
this.logger.debug(`L2 cache hit: ${fullKey}`);
return parsedValue;
}
this.logger.debug(`Cache miss: ${fullKey}`);
return null;
} catch (error) {
this.logger.error(`Cache get error: ${error.message}`, error.stack);
return null;
}
}
/**
* 设置缓存(多级)
*/
async set<T>(key: string, value: T, options?: MultiLevelCacheOptions): Promise<void> {
const fullKey = this.buildKey(key, options?.prefix);
try {
// 并行设置 L1 和 L2 缓存
await Promise.all([
this.l1Cache.set(fullKey, value, options?.l1Ttl || 60),
this.l2Cache.setex(fullKey, options?.l2Ttl || 300, JSON.stringify(value)),
]);
this.logger.debug(`Multi-level cache set: ${fullKey}`);
} catch (error) {
this.logger.error(`Multi-level cache set error: ${error.message}`, error.stack);
}
}
/**
* 删除缓存(多级)
*/
async del(key: string, options?: MultiLevelCacheOptions): Promise<void> {
const fullKey = this.buildKey(key, options?.prefix);
try {
// 并行删除 L1 和 L2 缓存
await Promise.all([
this.l1Cache.del(fullKey),
this.l2Cache.del(fullKey),
]);
this.logger.debug(`Multi-level cache del: ${fullKey}`);
} catch (error) {
this.logger.error(`Multi-level cache del error: ${error.message}`, error.stack);
}
}
/**
* 批量删除缓存
*/
async delPattern(pattern: string): Promise<void> {
try {
// 获取匹配的键
const keys = await this.l2Cache.keys(pattern);
if (keys.length > 0) {
// 删除 L2 缓存
await this.l2Cache.del(...keys);
// 删除 L1 缓存
const l1Promises = keys.map(key => this.l1Cache.del(key));
await Promise.allSettled(l1Promises);
this.logger.debug(`Multi-level cache del pattern: ${pattern}, deleted ${keys.length} keys`);
}
} catch (error) {
this.logger.error(`Multi-level cache del pattern error: ${error.message}`, error.stack);
}
}
/**
* 获取或设置缓存(缓存穿透保护)
*/
async getOrSet<T>(
key: string,
factory: () => Promise<T>,
options?: MultiLevelCacheOptions
): Promise<T> {
const fullKey = this.buildKey(key, options?.prefix);
try {
// 先尝试获取缓存
let value = await this.get<T>(fullKey, options);
if (value !== null) {
return value;
}
// 缓存未命中,执行工厂函数
value = await factory();
// 设置缓存
await this.set(key, value, options);
return value;
} catch (error) {
this.logger.error(`Multi-level cache getOrSet error: ${error.message}`, error.stack);
throw error;
}
}
/**
* 预热缓存
*/
async warmup<T>(
keys: string[],
factory: (key: string) => Promise<T>,
options?: MultiLevelCacheOptions
): Promise<void> {
this.logger.log(`Starting cache warmup for ${keys.length} keys`);
const promises = keys.map(async (key) => {
try {
const value = await factory(key);
await this.set(key, value, options);
this.logger.debug(`Cache warmed up: ${key}`);
} catch (error) {
this.logger.error(`Cache warmup failed for key ${key}: ${error.message}`);
}
});
await Promise.allSettled(promises);
this.logger.log('Cache warmup completed');
}
/**
* 获取缓存统计信息
*/
async getStats(): Promise<{
l1Stats: { size: number; hitRate: number };
l2Stats: { size: number; hitRate: number };
totalHitRate: number;
}> {
try {
// 获取 L2 缓存统计
const l2Info = await this.l2Cache.info('memory');
const l2Keys = await this.l2Cache.dbsize();
// 解析 L2 内存使用
const memoryMatch = l2Info.match(/used_memory_human:(\S+)/);
const l2Memory = memoryMatch ? memoryMatch[1] : '0B';
return {
l1Stats: {
size: 0, // 需要实现 L1 缓存大小统计
hitRate: 0, // 需要实现命中率统计
},
l2Stats: {
size: this.parseMemoryUsage(l2Memory),
hitRate: 0, // 需要实现命中率统计
},
totalHitRate: 0, // 需要实现总命中率统计
};
} catch (error) {
this.logger.error(`Multi-level cache stats error: ${error.message}`, error.stack);
return {
l1Stats: { size: 0, hitRate: 0 },
l2Stats: { size: 0, hitRate: 0 },
totalHitRate: 0,
};
}
}
/**
* 清空所有缓存
*/
async clear(): Promise<void> {
try {
// 直接使用 Redis 的 FLUSHDB 命令清空 L2 缓存
// L1 缓存会在下次访问时自动失效
await this.l2Cache.flushdb();
this.logger.debug('Multi-level cache cleared');
} catch (error) {
this.logger.error(`Multi-level cache clear error: ${error.message}`, error.stack);
}
}
/**
* 构建缓存键
*/
private buildKey(key: string, prefix?: string): string {
const finalPrefix = prefix ? `${this.appPrefix}:ml:${prefix}` : `${this.appPrefix}:ml`;
return `${finalPrefix}:${key}`;
}
/**
* 解析内存使用量
*/
private parseMemoryUsage(memoryStr: string): number {
const match = memoryStr.match(/^(\d+(?:\.\d+)?)([KMGT]?B)$/);
if (!match) return 0;
const [, value, unit] = match;
const numValue = parseFloat(value);
switch (unit) {
case 'KB': return numValue * 1024;
case 'MB': return numValue * 1024 * 1024;
case 'GB': return numValue * 1024 * 1024 * 1024;
case 'TB': return numValue * 1024 * 1024 * 1024 * 1024;
default: return numValue;
}
}
}