如何在Node.js Web应用程序中管理MongoDB连接?

时间:2012-05-18 16:42:34

标签: node.js mongodb database-connection connection-pooling

我正在使用MongoDB的node-mongodb-native驱动程序来编写网站。

我对如何管理连接有一些疑问:

  1. 仅针对所有请求使用一个MongoDB连接是否足够?有任何性能问题吗?如果没有,我可以设置全局连接以在整个应用程序中使用吗?

  2. 如果没有,如果我在请求到达时打开新连接并在处理请求时关闭它是否合适?打开和关闭连接是否昂贵?

  3. 我应该使用全局连接池吗?我听说驱动程序有一个本机连接池。这是一个不错的选择吗?

  4. 如果我使用连接池,应该使用多少个连接?

  5. 我还应注意其他事项吗?

14 个答案:

答案 0 :(得分:417)

The primary committer to node-mongodb-native says

  

当您的应用启动并重复使用时,您可以打开一次MongoClient.connect   db对象。它不是每个单独的连接池.connect   创建一个新的连接池。

因此,要直接回答您的问题,请重用MongoClient.connect()生成的db对象。这为您提供了池化,与每个数据库操作上的打开/关闭连接相比,它将提供明显的速度提升。

答案 1 :(得分:36)

在Node.js应用程序启动时打开一个新连接,并重用现有的for (i in df$Rainfall){ WD=data.frame(ifelse(df$Rainfall>0,1,0) } 连接对象:

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

来源:How to Open Database Connections in a Node.js/Express App

答案 2 :(得分:16)

以下是一些将管理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 documentation。看看吧。

答案 3 :(得分:11)

如果您有Express.js,您可以使用express-mongo-db缓存并在没有池的请求之间共享MongoDB连接(因为接受的答案表明这是共享连接的正确方法)。

如果没有 - 您可以查看其源代码并在另一个框架中使用它。

答案 4 :(得分:9)

在单个自包含模块中管理mongo连接池。这种方法有两个好处。首先,它使您的代码模块化,更容易测试。其次,您不必将数据库连接混合在请求对象中,而不是数据库连接对象的位置。 (考虑到JavaScript的本质,我认为将任何东西混合到由库代码构造的对象中是非常危险的)。因此,您只需要考虑一个导出两个方法的模块。 connect = () => Promiseget = () => 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;
}

答案 5 :(得分:6)

我一直在我的应用中使用带有redis连接的通用池 - 我强烈推荐它。它的通用和我肯定知道它适用于mysql所以我认为你不会有任何问题和mongo

https://github.com/coopernurse/node-pool

答案 6 :(得分:4)

http://mongoosejs.com/docs/api.html

查看猫鼬的来源。它们打开一个连接并将其绑定到Model对象,因此当需要模型Object时,将建立与DB的连接。驱动程序负责连接池。

答案 7 :(得分:2)

我在我的项目中实现了以下代码,以在我的代码中实现连接池,这样它将在我的项目中创建一个最小连接并重用可用连接

/* 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");

});

答案 8 :(得分:2)

您应该创建一个连接作为服务,然后在需要时重复使用它。

// 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

答案 9 :(得分:1)

实现连接池的最佳方法是创建一个全局数组变量,该变量保存带有MongoClient返回的连接对象的db名称,然后在需要联系数据库时重用该连接。

  1. 在您的Server.js中定义var global.dbconnections = [];

  2. 创建服务命名connectionService.js。它将有2个方法getConnection和createConnection。 因此,当用户调用getConnection()时,它将在全局连接变量中找到详细信息并返回连接详细信息(如果已存在),否则它将调用createConnection()并返回连接详细信息。

  3. 使用db_name调用此服务,它将返回连接对象(如果已有),它将创建新连接并将其返回给您。

  4. 希望有所帮助:)

    这是connectionService.js代码:

    var mongo = require('mongoskin');
    var mongodb = require('mongodb');
    var Q = require('q');
    var service = {};
    service.getConnection = getConnection ;
    module.exports = service;
    
    function getConnection(appDB){
        var deferred = Q.defer();
        var connectionDetails=global.dbconnections.find(item=>item.appDB==appDB)
    
        if(connectionDetails){deferred.resolve(connectionDetails.connection);
        }else{createConnection(appDB).then(function(connectionDetails){
                deferred.resolve(connectionDetails);})
        }
        return deferred.promise;
    }
    
    function createConnection(appDB){
        var deferred = Q.defer();
        mongodb.MongoClient.connect(connectionServer + appDB, (err,database)=> 
        {
            if(err) deferred.reject(err.name + ': ' + err.message);
            global.dbconnections.push({appDB: appDB,  connection: database});
            deferred.resolve(database);
        })
         return deferred.promise;
    } 
    

答案 10 :(得分:0)

如果使用Express,还有另一种更直接的方法,即利用Express的内置功能在应用程序内的路由和模块之间共享数据。有一个名为app.locals的对象。我们可以将属性附加到它并从我们的路线内部访问它。要使用它,请在app.js文件中实例化mongo连接。

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可以轻松访问它,并且不需要创建任何其他模块。

答案 11 :(得分:0)

如果有人想要在 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 未定义,那么您将收到错误。

答案 12 :(得分:0)

使用以下方法可以轻松管理尽可能多的连接

var mongoose = require('mongoose');


//Set up default mongoose connection
const bankDB = ()=>{
    return  mongoose.createConnection('mongodb+srv://<username>:<passwprd>@mydemo.jk4nr.mongodb.net/<database>?retryWrites=true&w=majority',options);
    
}

bankDB().then(()=>console.log('Connected to mongoDB-Atlas bankApp...'))
       .catch((err)=>console.error('Could not connected to mongoDB',err));
       
//Set up second mongoose connection
const myDB = ()=>{
    return  mongoose.createConnection('mongodb+srv://<username>:<password>@mydemo.jk4nr.mongodb.net/<database>?retryWrites=true&w=majority',options);
   
}
myDB().then(()=>console.log('Connected to mongoDB-Atlas connection 2...'))
       .catch((err)=>console.error('Could not connected to mongoDB',err));

module.exports = { bankDB(), myDB() };

答案 13 :(得分:-1)

mongodb.com->新项目->新集群->新集合->连接-> IP地址:0.0.0.0/0&db cred->连接应用程序->复制连接字符串并粘贴到。您的节点应用程序的环境文件,并确保用用户的实际密码替换“”,并用您的数据库名称替换“ / test”

创建新文件.env

CONNECTIONSTRING=x --> const client = new MongoClient(CONNECTIONSTRING) 
PORT=8080 
JWTSECRET=mysuper456secret123phrase