203 lines
5.5 KiB
JavaScript
203 lines
5.5 KiB
JavaScript
const mongoose = require('mongoose');
|
||
const redis = require('redis');
|
||
|
||
class DatabaseConnection {
|
||
constructor() {
|
||
this.mongooseConnection = null;
|
||
this.redisClient = null;
|
||
}
|
||
|
||
// 连接MongoDB
|
||
async connectMongoDB() {
|
||
try {
|
||
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/pdf-tools';
|
||
|
||
this.mongooseConnection = await mongoose.connect(mongoUri, {
|
||
useNewUrlParser: true,
|
||
useUnifiedTopology: true,
|
||
serverSelectionTimeoutMS: 5000,
|
||
maxPoolSize: 10,
|
||
socketTimeoutMS: 45000,
|
||
});
|
||
|
||
console.log('✅ MongoDB连接成功');
|
||
|
||
// 监听连接事件
|
||
mongoose.connection.on('error', (err) => {
|
||
console.error('❌ MongoDB连接错误:', err);
|
||
});
|
||
|
||
mongoose.connection.on('disconnected', () => {
|
||
console.log('⚠️ MongoDB连接断开');
|
||
});
|
||
|
||
mongoose.connection.on('reconnected', () => {
|
||
console.log('✅ MongoDB重新连接成功');
|
||
});
|
||
|
||
return this.mongooseConnection;
|
||
} catch (error) {
|
||
console.error('❌ MongoDB连接失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 连接Redis
|
||
async connectRedis() {
|
||
try {
|
||
const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379';
|
||
|
||
this.redisClient = redis.createClient({
|
||
url: redisUrl,
|
||
retry_strategy: (options) => {
|
||
if (options.error && options.error.code === 'ECONNREFUSED') {
|
||
console.error('❌ Redis服务器拒绝连接');
|
||
return new Error('Redis服务器拒绝连接');
|
||
}
|
||
if (options.total_retry_time > 1000 * 60 * 60) {
|
||
console.error('❌ Redis重连超时');
|
||
return new Error('Redis重连超时');
|
||
}
|
||
if (options.attempt > 10) {
|
||
console.error('❌ Redis重连次数超限');
|
||
return undefined;
|
||
}
|
||
// 重连间隔递增
|
||
return Math.min(options.attempt * 100, 3000);
|
||
}
|
||
});
|
||
|
||
this.redisClient.on('error', (err) => {
|
||
console.error('❌ Redis连接错误:', err);
|
||
});
|
||
|
||
this.redisClient.on('connect', () => {
|
||
console.log('✅ Redis连接成功');
|
||
});
|
||
|
||
this.redisClient.on('reconnecting', () => {
|
||
console.log('🔄 Redis重新连接中...');
|
||
});
|
||
|
||
this.redisClient.on('ready', () => {
|
||
console.log('✅ Redis准备就绪');
|
||
});
|
||
|
||
await this.redisClient.connect();
|
||
return this.redisClient;
|
||
} catch (error) {
|
||
console.error('❌ Redis连接失败:', error);
|
||
// Redis连接失败不应该阻止应用启动
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// 初始化所有数据库连接
|
||
async initialize() {
|
||
try {
|
||
// 并行连接数据库
|
||
const [mongoConnection, redisConnection] = await Promise.allSettled([
|
||
this.connectMongoDB(),
|
||
this.connectRedis()
|
||
]);
|
||
|
||
if (mongoConnection.status === 'rejected') {
|
||
throw new Error(`MongoDB连接失败: ${mongoConnection.reason.message}`);
|
||
}
|
||
|
||
if (redisConnection.status === 'rejected') {
|
||
console.warn('⚠️ Redis连接失败,将使用内存缓存');
|
||
}
|
||
|
||
console.log('🎉 数据库初始化完成');
|
||
return {
|
||
mongodb: mongoConnection.value,
|
||
redis: redisConnection.status === 'fulfilled' ? redisConnection.value : null
|
||
};
|
||
} catch (error) {
|
||
console.error('❌ 数据库初始化失败:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 关闭所有连接
|
||
async close() {
|
||
try {
|
||
const promises = [];
|
||
|
||
if (this.mongooseConnection) {
|
||
promises.push(mongoose.connection.close());
|
||
}
|
||
|
||
if (this.redisClient) {
|
||
promises.push(this.redisClient.quit());
|
||
}
|
||
|
||
await Promise.all(promises);
|
||
console.log('✅ 数据库连接已关闭');
|
||
} catch (error) {
|
||
console.error('❌ 关闭数据库连接失败:', error);
|
||
}
|
||
}
|
||
|
||
// 获取MongoDB连接状态
|
||
getMongoStatus() {
|
||
return {
|
||
status: mongoose.connection.readyState,
|
||
host: mongoose.connection.host,
|
||
port: mongoose.connection.port,
|
||
name: mongoose.connection.name
|
||
};
|
||
}
|
||
|
||
// 获取Redis连接状态
|
||
getRedisStatus() {
|
||
if (!this.redisClient) {
|
||
return { status: 'disconnected', message: '未连接' };
|
||
}
|
||
|
||
return {
|
||
status: this.redisClient.isReady ? 'connected' : 'disconnected',
|
||
message: this.redisClient.isReady ? '已连接' : '未连接'
|
||
};
|
||
}
|
||
|
||
// 健康检查
|
||
async healthCheck() {
|
||
const health = {
|
||
mongodb: { status: 'unknown', message: '' },
|
||
redis: { status: 'unknown', message: '' }
|
||
};
|
||
|
||
try {
|
||
// MongoDB健康检查
|
||
if (mongoose.connection.readyState === 1) {
|
||
await mongoose.connection.db.admin().ping();
|
||
health.mongodb = { status: 'healthy', message: '连接正常' };
|
||
} else {
|
||
health.mongodb = { status: 'unhealthy', message: '连接异常' };
|
||
}
|
||
} catch (error) {
|
||
health.mongodb = { status: 'unhealthy', message: error.message };
|
||
}
|
||
|
||
try {
|
||
// Redis健康检查
|
||
if (this.redisClient && this.redisClient.isReady) {
|
||
await this.redisClient.ping();
|
||
health.redis = { status: 'healthy', message: '连接正常' };
|
||
} else {
|
||
health.redis = { status: 'unhealthy', message: '连接异常' };
|
||
}
|
||
} catch (error) {
|
||
health.redis = { status: 'unhealthy', message: error.message };
|
||
}
|
||
|
||
return health;
|
||
}
|
||
}
|
||
|
||
// 创建单例实例
|
||
const databaseConnection = new DatabaseConnection();
|
||
|
||
module.exports = databaseConnection; |