You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose二次请求报错:Connection was force closed问题求助

MongooseError: Connection was force closed 问题排查与解决

问题原因

  1. 全局连接的竞态冲突:Mongoose默认使用全局单例连接,每次请求调用mongoose.disconnect()会关闭全局连接,二次请求时可能前一次的断开操作未完成就执行新的connect,导致连接状态混乱,查询时连接已被强制关闭。
  2. 模型与连接绑定不匹配:导入的Company、Admin等模型默认绑定Mongoose全局连接,切换数据库时模型未重新绑定到新连接,查询时仍使用已关闭的旧连接。
  3. 无意义的频繁断连:每次请求都断开全局连接会导致连接频繁创建销毁,既损耗性能,又容易引发连接状态异常。

解决方案

核心思路是使用Mongoose多连接管理,为每个数据库维护独立连接实例,避免操作全局连接,同时让模型动态绑定对应连接。

步骤1:重构模型为动态绑定模式

修改所有模型文件(以Company.js为例),改为导出接收连接实例的工厂函数:

// models/Company.js
const mongoose = require('mongoose');

const companySchema = new mongoose.Schema({
  email: String,
  password: String,
  // 其他字段定义
});

// 传入连接实例创建模型
module.exports = (connection) => {
  return connection.model('Company', companySchema);
};

Admin、Employee等其他模型执行同样修改。

步骤2:修改中间件实现多连接缓存与复用

不再操作全局连接,而是为每个数据库创建/复用独立连接,动态绑定模型:

const jwt = require('jsonwebtoken');
const JWT_SECRET = "PASSjajghjklnaslg";
const mongoose = require("mongoose");
// 导入模型工厂函数
const createCompanyModel = require("../models/Company");
const createAdminModel = require('../models/Admin');
const createEmployeeModel = require('../models/Employee');
const createManagerModel = require('../models/Manager');
const createSubAdminModel = require('../models/SubAdmin');
const URI = "mongodb://0.0.0.0:27017/";

// 缓存已创建的连接,避免重复初始化
const connectionCache = new Map();

const connectToDB = async (req, res, next) => {
    const token = req.header("auth-token");

    if (!token) {
        next();
        return;
    }
    try {
        const data = jwt.verify(token, JWT_SECRET);
        const dbName = data.companyEmail.replace(/[.]/g, '%2e');
        const dbURI = `${URI}${encodeURIComponent(dbName)}`;

        // 校验数据库是否存在
        const adminDb = mongoose.connection.db.admin();
        const result = await adminDb.listDatabases();
        const dbsNames = result.databases.map(db => db.name);
        if (!dbsNames.includes(dbName)) {
            res.status(401).send("Access denied");
            return;
        }

        // 获取或创建数据库连接
        let connection;
        if (connectionCache.has(dbName)) {
            connection = connectionCache.get(dbName);
            // 连接不可用时重新创建
            if (connection.readyState !== 1) {
                connection = await mongoose.createConnection(dbURI, {
                    useNewUrlParser: true,
                    useUnifiedTopology: true
                });
                connectionCache.set(dbName, connection);
            }
        } else {
            connection = await mongoose.createConnection(dbURI, {
                useNewUrlParser: true,
                useUnifiedTopology: true
            });
            connectionCache.set(dbName, connection);
        }

        // 动态创建模型并执行业务逻辑
        if (data.accountType === "company") {
            const Company = createCompanyModel(connection);
            const company = await Company.findById(data.company).select("-password");
            const anyDoc = await Company.findOne({});

            if (!company && !anyDoc) {
                await connection.db.dropDatabase();
                connectionCache.delete(dbName); // 清理无效连接缓存
                res.status(401).send("Access denied");
                return;
            }
            if (!company) {
                res.status(401).send("Access denied");
                return;
            }

            res.locals.company = company;
            next();
            return;
        } else if (["admin", "subAdmin", "manager", "employee"].includes(data.accountType)) {
            let createModel;
            switch (req.query.role) {
                case "admin":
                    createModel = createAdminModel;
                    break;
                case "subAdmin":
                    createModel = createSubAdminModel;
                    break;
                case "manager":
                    createModel = createManagerModel;
                    break;
                case "employee":
                    createModel = createEmployeeModel;
                    break;
                default:
                    return res.status(400).send("Invalid role");
            }

            const Account = createModel(connection);
            const account = await Account.findById(data.user).select("-password");

            if (!account) {
                res.status(401).send("Access denied");
                return;
            }

            res.locals.company = account;
            next();
            return;
        } else {
            return res.status(400).send('bad request');
        }

    } catch (err) {
        console.log(err);
        res.status(401).send({
            errors: ["Access denied"]
        });
        return;
    }
};

module.exports = connectToDB;

额外优化建议

  • 连接缓存清理:定期检查缓存中连接的活跃状态,移除长时间未使用的连接,避免资源占用。
  • 禁用全局连接操作:确保项目其他代码不再调用mongoose.connect()或mongoose.disconnect(),所有数据库操作均使用缓存的连接实例。
  • 增强错误处理:为连接创建过程添加更细致的错误捕获,比如连接超时、认证失败等场景的针对性处理。

内容的提问来源于stack exchange,提问作者Quiet Coder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 09:32:09