cursor-init/src/plugins/redis/redis.service.ts

399 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file Redis服务类
* @author hotok
* @date 2025-06-29
* @lastEditor hotok
* @lastEditTime 2025-06-29
* @description 精简的Redis连接服务类支持连接管理、状态跟踪和健康检查
*/
import { createClient } from 'redis';
import { redisConfig, getRedisUrl } from '@/config/redis.config';
import { Logger } from '@/plugins/logger/logger.service';
import type {
RedisConnectionStatus,
RedisConnectionInfo,
RedisHealthCheckResult
} from '@/type/redis.type';
/**
* Redis服务类
* 使用单例模式管理Redis连接
*/
export class RedisService {
/** 单例实例 */
private static instance: RedisService | null = null;
/** Redis客户端实例 */
private _client: any = null;
/** 连接状态信息 */
private _connectionInfo: RedisConnectionInfo;
/** 初始化标志 */
private _isInitialized = false;
/**
* 私有构造函数,防止外部实例化
*/
private constructor() {
this._connectionInfo = {
status: 'disconnected',
host: redisConfig.host,
port: redisConfig.port,
database: redisConfig.database,
connectName: redisConfig.connectName,
isConnected: false,
};
}
/**
* 获取单例实例
*/
public static getInstance(): RedisService {
if (!RedisService.instance) {
RedisService.instance = new RedisService();
}
return RedisService.instance;
}
/**
* 获取Redis客户端实例
*/
public get client(): any {
if (!this._client) {
throw new Error('Redis未初始化请先调用 initialize() 方法');
}
return this._client;
}
/**
* 获取连接状态信息
*/
public get connectionInfo(): RedisConnectionInfo {
return { ...this._connectionInfo };
}
/**
* 检查是否已初始化
*/
public get isInitialized(): boolean {
return this._isInitialized;
}
/**
* 验证Redis配置
*/
private validateConfig(): void {
if (!redisConfig.host || !redisConfig.port) {
throw new Error('Redis配置无效缺少host或port');
}
if (redisConfig.port < 1 || redisConfig.port > 65535) {
throw new Error(`Redis端口号无效: ${redisConfig.port}`);
}
}
/**
* 更新连接状态
*/
private updateConnectionStatus(status: RedisConnectionStatus, error?: string): void {
this._connectionInfo.status = status;
this._connectionInfo.error = error;
this._connectionInfo.isConnected = status === 'connected';
if (status === 'connected') {
this._connectionInfo.connectedAt = new Date();
this._connectionInfo.error = undefined;
}
}
/**
* 初始化Redis连接
*/
public async initialize(): Promise<any> {
// 防止重复初始化
if (this._isInitialized && this._client) {
Logger.info('Redis 已初始化,返回现有实例');
return this._client;
}
try {
this.validateConfig();
this.updateConnectionStatus('connecting');
// 创建Redis客户端
this._client = createClient({
name: redisConfig.connectName,
username: redisConfig.username,
password: redisConfig.password,
database: redisConfig.database,
url: getRedisUrl(),
});
// 连接Redis
await this._client.connect();
// 测试连接
await this._client.ping();
this._isInitialized = true;
this.updateConnectionStatus('connected');
Logger.info({
message: 'Redis 初始化成功',
host: redisConfig.host,
port: redisConfig.port,
database: redisConfig.database,
connectName: redisConfig.connectName,
});
return this._client;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
this.updateConnectionStatus('error', errorMessage);
Logger.error(new Error(`Redis 初始化失败: ${errorMessage}`));
throw new Error(`Redis 初始化失败: ${errorMessage}`);
}
}
/**
* 检查连接状态
*/
public async checkConnection(): Promise<boolean> {
try {
if (!this._client || !this._connectionInfo.isConnected) {
return false;
}
await this._client.ping();
return true;
} catch (error) {
Logger.error(error instanceof Error ? error : new Error('Redis连接检查失败'));
return false;
}
}
/**
* 执行健康检查
*/
public async healthCheck(): Promise<RedisHealthCheckResult> {
const startTime = Date.now();
try {
if (!this._client) {
return {
status: 'unhealthy',
responseTime: 0,
connectionInfo: this.connectionInfo,
error: 'Redis客户端未初始化',
};
}
// 执行ping测试
await this._client.ping();
const responseTime = Date.now() - startTime;
return {
status: 'healthy',
responseTime,
connectionInfo: this.connectionInfo,
};
} catch (error) {
const responseTime = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
return {
status: 'unhealthy',
responseTime,
connectionInfo: this.connectionInfo,
error: errorMessage,
};
}
}
/**
* 优雅关闭连接
*/
public async close(): Promise<void> {
try {
if (this._client && this._connectionInfo.isConnected) {
await this._client.quit();
this._client = null;
this.updateConnectionStatus('disconnected');
this._isInitialized = false;
Logger.info('Redis连接已关闭');
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
Logger.error(new Error(`关闭Redis连接时出错: ${errorMessage}`));
throw new Error(`关闭Redis连接失败: ${errorMessage}`);
}
}
/**
* 重新连接
*/
public async reconnect(): Promise<any> {
Logger.info('正在重新连接Redis...');
// 先关闭现有连接
await this.close();
// 重新初始化连接
return await this.initialize();
}
}
/**
* ==============================================
* 主要导出 - 推荐使用的API
* ==============================================
*/
/**
* Redis服务单例实例
*
* @description 获取RedisService的单例实例推荐的使用方式
* @example
* ```typescript
* import { redisService } from '@/plugins/redis/redis.service';
*
* // 初始化Redis
* await redisService.initialize();
*
* // 获取客户端实例
* const client = redisService.client;
*
* // 检查连接状态
* const isConnected = await redisService.checkConnection();
* ```
*/
export const redisService = RedisService.getInstance();
/**
* ==============================================
* 向后兼容导出 - 保持原有函数式API
* ==============================================
*/
/**
* 创建并初始化Redis连接
*
* @description 向后兼容的初始化方法内部调用redisService.initialize()
* @returns {Promise<any>} 返回初始化后的Redis客户端实例
*
* @example
* ```typescript
* import { createRedisClient } from '@/plugins/redis/redis.service';
*
* const client = await createRedisClient();
* ```
*
* @deprecated 推荐使用 redisService.initialize() 替代
*/
export const createRedisClient = () => redisService.initialize();
/**
* 获取Redis连接状态信息
*
* @description 向后兼容的状态获取方法内部调用redisService.connectionInfo
* @returns {RedisConnectionInfo} 返回Redis连接状态信息
*
* @example
* ```typescript
* import { getRedisConnectionInfo } from '@/plugins/redis/redis.service';
*
* const info = getRedisConnectionInfo();
* console.log(`Redis状态: ${info.status}`);
* ```
*
* @deprecated 推荐使用 redisService.connectionInfo 替代
*/
export const getRedisConnectionInfo = () => redisService.connectionInfo;
/**
* 检查Redis连接状态
*
* @description 向后兼容的连接检查方法内部调用redisService.checkConnection()
* @returns {Promise<boolean>} 返回连接是否正常
*
* @example
* ```typescript
* import { checkRedisConnection } from '@/plugins/redis/redis.service';
*
* const isConnected = await checkRedisConnection();
* if (!isConnected) {
* console.log('Redis连接异常');
* }
* ```
*
* @deprecated 推荐使用 redisService.checkConnection() 替代
*/
export const checkRedisConnection = () => redisService.checkConnection();
/**
* 优雅关闭Redis连接
*
* @description 向后兼容的连接关闭方法内部调用redisService.close()
* @returns {Promise<void>} 返回关闭操作的Promise
*
* @example
* ```typescript
* import { closeRedisConnection } from '@/plugins/redis/redis.service';
*
* // 应用关闭时清理资源
* process.on('SIGTERM', async () => {
* await closeRedisConnection();
* process.exit(0);
* });
* ```
*
* @deprecated 推荐使用 redisService.close() 替代
*/
export const closeRedisConnection = () => redisService.close();
/**
* 重新连接Redis
*
* @description 向后兼容的重连方法内部调用redisService.reconnect()
* @returns {Promise<any>} 返回重新连接后的客户端实例
*
* @example
* ```typescript
* import { reconnectRedis } from '@/plugins/redis/redis.service';
*
* try {
* const client = await reconnectRedis();
* console.log('Redis重连成功');
* } catch (error) {
* console.error('Redis重连失败:', error);
* }
* ```
*
* @deprecated 推荐使用 redisService.reconnect() 替代
*/
export const reconnectRedis = () => redisService.reconnect();
/**
* 获取Redis客户端实例
*
* @description 向后兼容的客户端获取方法内部调用redisService.client
* @returns {any} 返回Redis客户端实例
* @throws {Error} 如果Redis未初始化则抛出错误
*
* @example
* ```typescript
* import { redis } from '@/plugins/redis/redis.service';
*
* // 确保先初始化
* await createRedisClient();
*
* // 获取客户端实例
* const client = redis();
* const result = await client.get('key');
* ```
*
* @deprecated 推荐使用 redisService.client 替代
*/
export const redis = () => redisService.client;