阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 处理连接错误与重连机制

处理连接错误与重连机制

作者:陈川 阅读数:8021人阅读 分类: MongoDB

连接错误的原因分析

Mongoose连接MongoDB数据库时可能遇到多种错误情况。常见的错误类型包括:

  1. 网络问题:服务器不可达、DNS解析失败、端口被防火墙阻挡
  2. 认证失败:用户名/密码错误、权限不足
  3. 服务器配置问题:MongoDB服务未启动、连接数达到上限
  4. 超时问题:网络延迟导致连接超时
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

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌