Files
pdf-tools/server/config/database.js

203 lines
5.5 KiB
JavaScript
Raw Permalink 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.

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;