处理连接错误与重连机制
连接错误的原因分析
Mongoose连接MongoDB数据库时可能遇到多种错误情况。常见的错误类型包括:
- 网络问题:服务器不可达、DNS解析失败、端口被防火墙阻挡
- 认证失败:用户名/密码错误、权限不足
- 服务器配置问题:MongoDB服务未启动、连接数达到上限
- 超时问题:网络延迟导致连接超时
mongoose.connect('mongodb://localhost:27017/mydb', {
auth: { username: 'user', password: 'wrongpassword' }
}).catch(err => {
console.error('认证失败:', err.message);
// 输出:认证失败: Authentication failed
});
基础错误处理机制
Mongoose提供了多种方式捕获连接错误。最基本的错误处理是在connect()方法后使用catch:
mongoose.connect(uri)
.then(() => console.log('连接成功'))
.catch(err => console.error('连接失败:', err));
也可以监听connection对象的error事件:
const db = mongoose.connection;
db.on('error', (err) => {
console.error('数据库连接错误:', err.message);
});
自动重连实现方案
Mongoose内置了自动重连机制,但需要合理配置参数:
mongoose.connect(uri, {
autoReconnect: true, // 已弃用,Mongoose 5.x+默认启用
reconnectTries: Number.MAX_VALUE, // 无限重试
reconnectInterval: 1000, // 每隔1秒重试
bufferCommands: false // 连接断开时立即报错而不是缓冲命令
});
更精细的控制可以通过监听事件实现:
const db = mongoose.connection;
db.on('disconnected', () => {
console.log('连接断开,尝试重连...');
mongoose.connect(uri, { useNewUrlParser: true });
});
db.on('reconnected', () => {
console.log('成功重新连接到数据库');
});
db.on('reconnectFailed', () => {
console.error('重连失败,请检查数据库状态');
});
高级重连策略
对于生产环境,建议实现指数退避算法:
let retryAttempts = 0;
const MAX_RETRIES = 5;
const BASE_DELAY = 1000;
function connectWithRetry() {
return mongoose.connect(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
}).catch(err => {
if (retryAttempts < MAX_RETRIES) {
const delay = BASE_DELAY * Math.pow(2, retryAttempts);
retryAttempts++;
console.log(`连接失败,${delay}ms后重试...`);
return new Promise(resolve =>
setTimeout(resolve, delay)
).then(() => connectWithRetry());
}
throw err;
});
}
connectWithRetry().then(() => {
console.log('最终连接成功');
});
连接状态管理
维护应用状态时需要正确处理连接状态:
let isConnected = false;
let isConnecting = false;
mongoose.connection.on('connected', () => {
isConnected = true;
isConnecting = false;
});
mongoose.connection.on('disconnected', () => {
isConnected = false;
});
mongoose.connection.on('connecting', () => {
isConnecting = true;
});
function ensureConnection() {
if (isConnected) return Promise.resolve();
if (isConnecting) {
return new Promise((resolve) => {
const check = () => {
if (isConnected) resolve();
else setTimeout(check, 100);
};
check();
});
}
return connectWithRetry();
}
事务中的连接处理
使用事务时需要特别注意连接状态:
async function performTransaction() {
const session = await mongoose.startSession();
session.startTransaction();
try {
await ensureConnection(); // 确保连接正常
// 执行事务操作
await Model1.create([{...}], { session });
await Model2.updateMany({...}, {...}, { session });
await session.commitTransaction();
} catch (err) {
await session.abortTransaction();
throw err;
} finally {
session.endSession();
}
}
集群环境下的连接处理
处理副本集或分片集群时需要考虑更多因素:
mongoose.connect('mongodb://node1,node2,node3/dbname?replicaSet=myReplSet', {
useNewUrlParser: true,
useUnifiedTopology: true,
readPreference: 'secondaryPreferred',
connectTimeoutMS: 30000,
socketTimeoutMS: 30000,
serverSelectionTimeoutMS: 5000,
heartbeatFrequencyMS: 10000,
retryWrites: true,
retryReads: true
});
mongoose.connection.on('fullsetup', () => {
console.log('已连接到副本集所有节点');
});
mongoose.connection.on('all', () => {
console.log('已连接到所有可用节点');
});
连接池优化
合理配置连接池提升性能:
mongoose.connect(uri, {
poolSize: 10, // 默认5
maxPoolSize: 20,
minPoolSize: 2,
socketTimeoutMS: 45000,
waitQueueTimeoutMS: 10000,
maxIdleTimeMS: 30000
});
监控连接池状态:
setInterval(() => {
const poolStats = mongoose.connection.getClient().s.topology.pool;
console.log({
available: poolStats.availableConnections,
waiting: poolStats.waiting,
active: poolStats.active
});
}, 5000);
测试环境下的特殊处理
测试时可能需要不同的连接策略:
function getConnectionOptions() {
if (process.env.NODE_ENV === 'test') {
return {
autoIndex: false,
connectTimeoutMS: 5000,
socketTimeoutMS: 5000,
serverSelectionTimeoutMS: 5000,
retryWrites: false
};
}
return {
autoIndex: true,
connectTimeoutMS: 30000,
socketTimeoutMS: 30000,
serverSelectionTimeoutMS: 30000,
retryWrites: true
};
}
mongoose.connect(uri, getConnectionOptions());
云服务环境适配
针对MongoDB Atlas等云服务的特殊配置:
mongoose.connect(process.env.ATLAS_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
retryWrites: true,
w: 'majority',
ssl: true,
sslValidate: true,
sslCA: require('fs').readFileSync('./ca.pem'),
readConcern: { level: 'majority' },
writeConcern: { w: 'majority', j: true }
});
连接健康检查
实现主动健康检查机制:
async function checkConnectionHealth() {
try {
await mongoose.connection.db.admin().ping();
return { healthy: true };
} catch (err) {
return {
healthy: false,
error: err.message,
stack: err.stack
};
}
}
// 定期检查
setInterval(async () => {
const health = await checkConnectionHealth();
if (!health.healthy) {
console.error('数据库健康检查失败:', health.error);
// 触发重连逻辑
}
}, 30000);
多数据库连接处理
同时管理多个数据库连接:
const primaryDB = mongoose.createConnection(primaryURI, {
useNewUrlParser: true,
retryWrites: true
});
const secondaryDB = mongoose.createConnection(secondaryURI, {
useNewUrlParser: true,
readPreference: 'secondary'
});
primaryDB.on('error', handlePrimaryDBError);
secondaryDB.on('error', handleSecondaryDBError);
function handlePrimaryDBError(err) {
console.error('主数据库错误:', err);
setTimeout(() => {
primaryDB.openUri(primaryURI);
}, 5000);
}
连接关闭与清理
正确关闭连接释放资源:
async function gracefulShutdown() {
try {
await mongoose.connection.close(false); // force参数设为false
console.log('MongoDB连接已正常关闭');
process.exit(0);
} catch (err) {
console.error('关闭连接时出错:', err);
process.exit(1);
}
}
process.on('SIGINT', gracefulShutdown);
process.on('SIGTERM', gracefulShutdown);
日志与监控
实现详细的连接日志记录:
mongoose.set('debug', function(collectionName, methodName, ...methodArgs) {
console.log(`Mongoose: ${collectionName}.${methodName}`, methodArgs);
});
// 自定义监控
const connectionEvents = ['connecting', 'connected', 'disconnecting', 'disconnected', 'close', 'reconnected', 'error'];
connectionEvents.forEach(event => {
mongoose.connection.on(event, () => {
logConnectionStatus(event);
sendMetricsToMonitoringSystem(event);
});
});
function logConnectionStatus(event) {
console.log(`连接状态变更: ${event} at ${new Date().toISOString()}`);
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn