Mongodb与Nodejs的模块机制
Mongodb与Nodejs的模块机制
我做了一个测试页面:两个静态图片,一段取自mongodb的文字。然后不停的刷新网页。出现奇怪的现象: 我用如下代码连接数据库:
var mongodb=require('mongodb')
, mongoserver=new mongodb.Server('localhost',27017)
,dbtest=new mongodb.Db('test',mongoserver,{w:-1});
如果我将这些语句写在路由的响应函数里就没问题。但是如果我将以上代码写在一个js文件中,封装成模块,然后引用,即一下这样:
模块文件:
var mongodb=require('mongodb')
, mongoserver=new mongodb.Server('localhost',27017)
,dbtest=new mongodb.Db('test',mongoserver,{w:-1});
module.exports=dbtest;
路由响应函数中调用:
var dbtest=require('dbcon');
(其他代码都相同)就会出现 Error: db object already connecting, open cannot be called multiple times 错误。
请问这是怎么回事呢,封装到模块和写在响应函数里不一样吗?代码都是一样的啊?
在Node.js中,模块机制是一种重要的特性,它帮助我们组织和重用代码。当涉及到MongoDB这样的数据库时,如何正确地管理数据库连接变得尤为重要。在您的例子中,问题主要出在对数据库连接对象的重复初始化上。
问题分析
在您的代码中,您尝试将数据库连接逻辑封装到一个单独的模块中。然而,数据库连接对象(如Db
实例)不应该被多次初始化。一旦连接创建并打开,再次尝试打开同一个连接会导致错误。
解决方案
为了解决这个问题,您需要确保数据库连接只被初始化一次,并且在整个应用生命周期内保持该连接。您可以使用单例模式来实现这一点。
示例代码
数据库连接模块 (dbcon.js
)
// dbcon.js
let db;
module.exports = {
connect: async () => {
if (db) return db; // 如果已经连接,则直接返回
const MongoClient = require('mongodb').MongoClient;
const url = 'mongodb://localhost:27017';
const dbName = 'test';
try {
const client = new MongoClient(url, { useNewUrlParser: true, useUnifiedTopology: true });
await client.connect();
console.log("Connected successfully to server");
db = client.db(dbName);
return db;
} catch (err) {
console.error("Failed to connect", err);
}
}
};
路由响应函数 (app.js
或类似文件)
// app.js
const express = require('express');
const dbcon = require('./dbcon');
const app = express();
app.get('/', async (req, res) => {
try {
const db = await dbcon.connect();
const collection = db.collection('yourCollectionName');
const doc = await collection.findOne({ /* 查询条件 */ });
res.send(doc.text); // 假设文档有一个名为text的字段
} catch (err) {
res.status(500).send("Database error");
}
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
解释
- 单例模式:通过检查
db
变量是否已定义,我们避免了重复连接数据库。 - 异步操作:使用
async/await
语法处理异步操作,使代码更易读。 - 错误处理:添加了基本的错误处理逻辑,以确保在发生错误时能够适当地处理它们。
通过这种方式,您可以确保数据库连接只被初始化一次,并且在整个应用程序运行期间保持连接。
贴一下你的出错 stack
每个业务方法都有个基础类,在基础类里面 var dbtest=require(‘dbcon’); 一次就行
用MongoClient, 采用异步将数据库赋给宿主的方式连接数据库
‘use strict’;
var MongoClient = require(‘mongodb’).MongoClient;
function db(app) {
console.log(‘db required’);
MongoClient.connect(“mongodb://localhost:27017/mydb”, function (err, database) {
if (err) {
console.log(err);
} else {
app.db = database; // 将数据库对象赋值给宿主对象
if (app.onDatabaseReady) {
app.onDatabaseReady(database); //触发宿主对象中需要在数据库连接成功后运行的代码
}
console.log(“Connected to Database”);
}
});
}
module.exports = db;
主程序:
var db = require(’…/path/to/db’);
db(app);
…
app.db.xxxx
Error: db object already connecting, open cannot be called multiple times
at Db.open (X:\BYSJ\Project\simulat\node_modules\mongodb\lib\mongodb\db.js:224:11)
at exports.index (X:\BYSJ\Project\simulat\routes\index.js:16:12)
at callbacks (X:\BYSJ\Project\simulat\node_modules\express\lib\router\index.js:161:37)
at param (X:\BYSJ\Project\simulat\node_modules\express\lib\router\index.js:135:11)
at pass (X:\BYSJ\Project\simulat\node_modules\express\lib\router\index.js:142:5)
at Router._dispatch (X:\BYSJ\Project\simulat\node_modules\express\lib\router\index.js:170:5)
at Object.router (X:\BYSJ\Project\simulat\node_modules\express\lib\router\index.js:33:10)
at next (X:\BYSJ\Project\simulat\node_modules\express\node_modules\connect\lib\proto.js:190:15)
at Object.methodOverride [as handle] (X:\BYSJ\Project\simulat\node_modules\express\node_modules\connect\lib\middleware\methodOverride.js:37:5)
at next (X:\BYSJ\Project\simulat\node_modules\express\node_modules\connect\lib\proto.js:190:15)
我是只var dbtest=require(‘dbcon’);了一次,但是在并发访问的时候就出错了?
这个MongoClient是一个数据库驱动还是一种连接方法?
这个问题涉及到MongoDB和Node.js中的模块机制以及数据库连接对象的生命周期管理。
当你在路由响应函数内部创建数据库连接时,每次请求都会创建一个新的连接对象。这虽然会消耗更多的资源,但在这种情况下不会导致任何问题,因为每次请求都是独立的。
然而,当你将数据库连接代码封装成一个模块并将其导出时,模块在应用启动时只初始化一次。这意味着dbtest
对象在整个应用生命周期中只会被创建一次,并且只允许建立一次连接。如果你在多个请求中尝试多次使用这个对象,就会导致错误,因为数据库连接对象不能重复打开。
为了修复这个问题,你应该在模块中使用单一的数据库连接,并在需要时打开它。以下是一个改进的版本:
模块文件 (db.js)
const { MongoClient } = require('mongodb');
let dbConnection;
exports.connect = async () => {
if (!dbConnection) {
try {
const client = new MongoClient('mongodb://localhost:27017', { useNewUrlParser: true, useUnifiedTopology: true });
await client.connect();
console.log("Connected to MongoDB");
dbConnection = client.db('test');
} catch (err) {
console.error("Failed to connect to MongoDB", err);
}
}
return dbConnection;
};
路由响应函数中调用
const express = require('express');
const router = express.Router();
const { connect } = require('./db');
router.get('/', async (req, res) => {
try {
const db = await connect();
const collection = db.collection('yourCollectionName');
const data = await collection.findOne({}); // 假设你有一个文档来获取文字
res.render('index', { text: data.text }); // 将数据传递给视图
} catch (err) {
console.error("Error fetching data from MongoDB", err);
res.status(500).send("Internal Server Error");
}
});
module.exports = router;
在这个例子中,connect
函数确保只有一个数据库连接存在,并且在每次请求中都可以安全地使用该连接来执行查询。