我正在使用node-mongodb-native驱动程序和MongoDB编写一个网站。
我有关于如何管理连接的几个问题:
在所有请求中只使用一个MongoDB连接是否足够?这样做会有性能问题吗?如果可以,我可以设置一个全局连接来在整个应用程序中使用吗?
如果不行,当请求到达时开启新连接然后在处理完请求后关闭它是否好?打开和关闭连接是否昂贵?
应该使用全局连接池吗?我听说这个驱动程序已经具备原生连接池了。这是一个好选择吗?
如果我使用连接池,需要使用多少个连接?
还有其他需要注意的事项吗?
我正在使用node-mongodb-native驱动程序和MongoDB编写一个网站。
我有关于如何管理连接的几个问题:
在所有请求中只使用一个MongoDB连接是否足够?这样做会有性能问题吗?如果可以,我可以设置一个全局连接来在整个应用程序中使用吗?
如果不行,当请求到达时开启新连接然后在处理完请求后关闭它是否好?打开和关闭连接是否昂贵?
应该使用全局连接池吗?我听说这个驱动程序已经具备原生连接池了。这是一个好选择吗?
如果我使用连接池,需要使用多少个连接?
还有其他需要注意的事项吗?
你可以在应用程序启动时使用 MongoClient.connect 一次,并重复使用db对象。它不是单例连接池,每个 .connect 都会创建一个新的连接池。
因此,为了直接回答您的问题,请重用从MongoClient.connect()
返回的 db 对象。这将提供连接池,并与在每个数据库操作上打开/关闭连接相比,将提供明显的速度提升。
当 Node.js 应用程序启动时,打开一个新连接并重用现有的 db
连接对象:
/server.js
import express from 'express';
import Promise from 'bluebird';
import logger from 'winston';
import { MongoClient } from 'mongodb';
import config from './config';
import usersRestApi from './api/users';
const app = express();
app.use('/api/users', usersRestApi);
app.get('/', (req, res) => {
res.send('Hello World');
});
// Create a MongoDB connection pool and start the application
// after the database connection is ready
MongoClient.connect(config.database.url, { promiseLibrary: Promise }, (err, db) => {
if (err) {
logger.warn(`Failed to connect to the database. ${err.stack}`);
}
app.locals.db = db;
app.listen(config.port, () => {
logger.info(`Node.js app is listening at http://localhost:${config.port}`);
});
});
/api/users.js
import { Router } from 'express';
import { ObjectID } from 'mongodb';
const router = new Router();
router.get('/:id', async (req, res, next) => {
try {
const db = req.app.locals.db;
const id = new ObjectID(req.params.id);
const user = await db.collection('user').findOne({ _id: id }, {
email: 1,
firstName: 1,
lastName: 1
});
if (user) {
user.id = req.params.id;
res.send(user);
} else {
res.sendStatus(404);
}
} catch (err) {
next(err);
}
});
export default router;
这里有一些代码可以管理您的MongoDB连接。
var MongoClient = require('mongodb').MongoClient;
var url = require("../config.json")["MongoDBURL"]
var option = {
db:{
numberOfRetries : 5
},
server: {
auto_reconnect: true,
poolSize : 40,
socketOptions: {
connectTimeoutMS: 500
}
},
replSet: {},
mongos: {}
};
function MongoPool(){}
var p_db;
function initPool(cb){
MongoClient.connect(url, option, function(err, db) {
if (err) throw err;
p_db = db;
if(cb && typeof(cb) == 'function')
cb(p_db);
});
return MongoPool;
}
MongoPool.initPool = initPool;
function getInstance(cb){
if(!p_db){
initPool(cb)
}
else{
if(cb && typeof(cb) == 'function')
cb(p_db);
}
}
MongoPool.getInstance = getInstance;
module.exports = MongoPool;
当您启动服务器时,请调用initPool
。require("mongo-pool").initPool();
然后在任何其他模块中,您可以执行以下操作:
var MongoPool = require("mongo-pool");
MongoPool.getInstance(function (db){
// Query your MongoDB database.
});
这基于MongoDB文档。请查看。
{
numberOfRetries: 5, // 重试次数
auto_reconnect: true, // 自动重连
poolSize: 40, // 连接池大小
connectTimeoutMS: 30000 // 连接超时时间(毫秒)
}
- Blair在单个自包含模块中管理mongo连接池。这种方法提供了两个好处。首先,它使您的代码模块化且更容易测试。其次,您不必将数据库连接混杂在请求对象中,因为这不是数据库连接对象所在的位置。(考虑到JavaScript的性质,我认为将任何内容混入库代码构造的对象中非常危险)。因此,您只需要考虑一个导出两个方法的模块。connect = () => Promise
和 get = () => dbConnectionObject
。
有了这样的模块,您可以首先连接到数据库。
// runs in boot.js or what ever file your application starts with
const db = require('./myAwesomeDbModule');
db.connect()
.then(() => console.log('database connected'))
.then(() => bootMyApplication())
.catch((e) => {
console.error(e);
// Always hard exit on a database connection error
process.exit(1);
});
当应用程序在运行时,只需调用get()
即可获取数据库连接。
const db = require('./myAwesomeDbModule');
db.get().find(...)... // I have excluded code here to keep the example simple
如果您按照以下方式设置数据库模块,不仅可以确保应用程序在没有数据库连接的情况下不会启动,还可以全局访问数据库连接池,并在没有连接时出错。
// myAwesomeDbModule.js
let connection = null;
module.exports.connect = () => new Promise((resolve, reject) => {
MongoClient.connect(url, option, function(err, db) {
if (err) { reject(err); return; };
resolve(db);
connection = db;
});
});
module.exports.get = () => {
if(!connection) {
throw new Error('Call connect first!');
}
return connection;
}
get()
时,我们获取的是连接池而不是单个连接。连接池,顾名思义,是数据库连接的逻辑集合。如果池中没有连接,则驱动程序将尝试打开一个连接。一旦该连接打开,它就会被使用并返回到池中。下次访问池时,可能会重用此连接。好处在于池将为我们管理连接,因此如果连接断开,我们可能永远不会知道,因为池将为我们打开一个新连接。 - Stewart您应该创建一个服务连接,然后在需要时重用它。
// db.service.js
import { MongoClient } from "mongodb";
import database from "../config/database";
const dbService = {
db: undefined,
connect: callback => {
MongoClient.connect(database.uri, function(err, data) {
if (err) {
MongoClient.close();
callback(err);
}
dbService.db = data;
console.log("Connected to database");
callback(null);
});
}
};
export default dbService;
我的App.js示例
// App Start
dbService.connect(err => {
if (err) {
console.log("Error: ", err);
process.exit(1);
}
server.listen(config.port, () => {
console.log(`Api runnning at ${config.port}`);
});
});
使用以下代码,在您想要的任何地方都可以使用它:
import dbService from "db.service.js"
const db = dbService.db
我在我的项目中实现了以下代码,以实现连接池,在我的代码中它将创建一个最小的连接并重复使用可用的连接。
/* Mongo.js*/
var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/yourdatabasename";
var assert = require('assert');
var connection=[];
// Create the database connection
establishConnection = function(callback){
MongoClient.connect(url, { poolSize: 10 },function(err, db) {
assert.equal(null, err);
connection = db
if(typeof callback === 'function' && callback())
callback(connection)
}
)
}
function getconnection(){
return connection
}
module.exports = {
establishConnection:establishConnection,
getconnection:getconnection
}
/*app.js*/
// establish one connection with all other routes will use.
var db = require('./routes/mongo')
db.establishConnection();
//you can also call with callback if you wanna create any collection at starting
/*
db.establishConnection(function(conn){
conn.createCollection("collectionName", function(err, res) {
if (err) throw err;
console.log("Collection created!");
});
};
*/
// anyother route.js
var db = require('./mongo')
router.get('/', function(req, res, next) {
var connection = db.getconnection()
res.send("Hello");
});
var app = express();
MongoClient.connect('mongodb://localhost:27017/')
.then(client =>{
const db = client.db('your-db');
const collection = db.collection('your-collection');
app.locals.collection = collection;
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
req.app.locals
,而无需创建和要求其他模块。app.get('/', (req, res) => {
const collection = req.app.locals.collection;
collection.find({}).toArray()
.then(response => res.status(200).json(response))
.catch(error => console.error(error));
});
req.app.locals.your-collection
轻松访问它,并且不需要创建任何其他模块。如果有人想要在2021年使用Typescript编写程序,这是我正在使用的:
import { MongoClient, Collection } from "mongodb";
const FILE_DB_HOST = process.env.FILE_DB_HOST as string;
const FILE_DB_DATABASE = process.env.FILE_DB_DATABASE as string;
const FILES_COLLECTION = process.env.FILES_COLLECTION as string;
if (!FILE_DB_HOST || !FILE_DB_DATABASE || !FILES_COLLECTION) {
throw "Missing FILE_DB_HOST, FILE_DB_DATABASE, or FILES_COLLECTION environment variables.";
}
const client = new MongoClient(FILE_DB_HOST, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
class Mongoose {
static FilesCollection: Collection;
static async init() {
const connection = await client.connect();
const FileDB = connection.db(FILE_DB_DATABASE);
Mongoose.FilesCollection = FileDB.collection(FILES_COLLECTION);
}
}
Mongoose.init();
export default Mongoose;
我相信如果请求发生得太早(在Mongo.init()
完成之前),会抛出错误,因为Mongoose.FilesCollection
将是未定义的。
import { Request, Response, NextFunction } from "express";
import Mongoose from "../../mongoose";
export default async function GetFile(req: Request, res: Response, next: NextFunction) {
const files = Mongoose.FilesCollection;
const file = await files.findOne({ fileName: "hello" });
res.send(file);
}
files.findOne({ ... })
并且Mongoose.FilesCollection
未定义,则会出现错误。