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

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


仅使用一个MongoDB连接处理所有请求就足够了吗?是否存在性能问题?如果没有,我可以设置一个全局连接以在整个应用程序中使用吗?
如果没有,我可以在请求到达时打开一个新的连接,并在处理请求时关闭它,这会很好吗?打开和关闭连接是否昂贵?
我应该使用全局连接池吗?我听说驱动程序具有本地连接池。这是一个好选择吗?
如果我使用连接池,应该使用多少个连接?
还有其他需要注意的地方吗?


评论

@IonicãBizãu,对不起,我很长一段时间没有使用nodejs了,但是我还没有看到它。感谢您的留言〜

连接类和Promise全局变量

#1 楼

node-mongodb-native的主要提交者说:


当应用程序启动并重新使用db对象时,只需打开一次do MongoClient.connect。每个连接不是一个单例连接池。connect
创建一个新的连接池。


因此,要直接回答您的问题,请重用MongoClient.connect()产生的db对象。与每个数据库操作上的打开/关闭连接相比,这可以为您提供缓冲,并且速度会明显提高。

评论


链接到MongoClient.connect()mongodb.github.io/node-mongodb-native/driver-articles/…

– AndrewJM
13年7月24日在17:19



这是正确的答案。接受的答案是非常错误的,因为它说要为每个请求打开一个连接池,然后在这样做之后将其关闭。糟糕的架构。

–萨拉什·莫哈帕特拉(Saransh Mohapatra)
13年8月13日在14:56

这是一个正确的答案。我的天哪,我每次做某事时都必须打开和关闭,而我的插件每小时就需要350K!这就像攻击我自己的服务器。

–马济耶尔
2013年9月30日,1:12

@Cracker:如果您有快速应用程序,则可以使用以下中间件将db对象保存到req.db中:github.com/floatdrop/express-mongo-db

– floatdrop
2014年8月8日在18:15

如果有多个数据库..我应该为每个数据库一起打开一个连接。如果不是,是否可以在需要时打开和关闭?

–阿曼·古普塔(Aman Gupta)
16-10-25在14:19

#2 楼

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;


来源:如何在Node.js / Express应用程序中打开数据库连接


评论


这将创建一个数据库连接...如果要利用池,则必须在每次使用时创建/关闭

– amcdnl
15年12月15日在15:01

题外话,这是我见过的最奇怪的NodeJS文件。

–业余爱好者
16年6月12日在4:04

以前从未听说过app.locals,但很高兴您在这里向我介绍了他们

– Z_z_Z
18/09/24在21:37

对我有很大帮助!我为每个请求创建/关闭数据库连接时都犯了错误,我的应用程序性能因此下降。

– Leandro Lima
19年3月1日17:00

#3 楼

这是一些用于管理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文档的。看看吧。

评论


从5.x开始更新:var option = {numberOfRetries:5,auto_reconnect:true,poolSize:40,connectTimeoutMS:30000};

–布莱尔
18年7月11日在1:40

#4 楼

在一个独立的模块中管理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()它需要DB连接。

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;
}


评论


非常有用,正是我想要的!

– agui
17年5月9日在8:27

更好的是,您可以摆脱connect()函数,让get()函数检查连接是否为空,如果连接为空,则为您调用connect。让get()总是返回一个承诺。这是我管理连接的方式,效果很好。这是单例模式的使用。

–java-addict301
17-6-27 at 11:52



@ java-addict301虽然该方法确实提供了更简化的API,但确实有两个缺点。第一个是没有定义的方法来检查连接错误。每当调用get时,您将必须在任何地方处理该内联。我喜欢通过数据库连接尽早失败,通常我不会在没有连接到数据库的情况下让应用程序启动。另一个问题是吞吐量。因为您没有活动的连接,所以您可能不得不在无法控制的第一个get()调用上等待更长的时间。可能会歪曲您的报告指标。

– Stewart
17年6月5日在3:23

@Stewart构建应用程序/服务的方式通常是在启动时从数据库检索配置。这样,如果无法访问数据库,则应用程序将无法启动。另外,由于第一个请求始终在启动时,因此该设计的指标没有问题。还要在单例中一次放置一个连接异常时抛出一个连接异常,它会抛出一个无法连接的明显错误。由于无论如何在使用连接时应用都需要捕获数据库错误,因此这不会导致任何额外的内联处理。

–java-addict301
17年6月6日在0:31



嗨@Ayan重要的是要注意,在这里,当我们调用get()时,我们得到的是连接池而不是单个连接。顾名思义,连接池是数据库连接的逻辑集合。如果池中没有连接,驱动程序将尝试打开一个。打开该连接后,它将被使用并返回到池中。下次访问该池时,可以重新使用此连接。这里的好处是,池将为我们管理我们的连接,因此如果连接断开,我们可能永远不会知道,因为池将为我们打开一个新连接。

– Stewart
17年8月24日在3:38

#5 楼

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

如果不是-您可以查看其源代码并在其他框架中使用它。

#6 楼

您应该将连接创建为服务,然后在需要时重用。

// 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无法连接,则MongoClient.close()会给出错误。但是对于原始问题是一个很好的解决方案。

–Himanshu
17年11月15日在7:09



#7 楼

我一直在我的应用程序中使用带有Redis连接的泛型池-我强烈建议这样做。它是通用的,我当然知道它可以与mysql一起使用,所以我认为您和mongo不会有任何问题。

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

评论


Mongo已经在驱动程序中进行了连接池,但是我已经将mongo连接映射到与节点池匹配的接口中,这样我的所有连接都遵循相同的模式,即使在mongo情况下,清理也不实际上触发任何东西。

–Tracker1
2014年8月11日在20:49

#8 楼

我已经在我的项目中实现了以下代码,以在我的代码中实现连接池,因此它将在我的项目中创建一个最低限度的连接并重用可用的连接

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

});


#9 楼

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


Server.js定义var global.dbconnections = [];
创建服务命名connectionService.js。它将具有2个方法getConnection和createConnection。
因此,当用户调用getConnection()时,它将在全局连接变量中查找详细信息并返回连接详细信息(如果已存在),否则它将调用createConnection()并返回连接详细信息。 br />使用db_name调用此服务,如果已有连接对象,它将返回连接对象,它将创建新的连接并将其返回给您。

希望对您有帮助:)

这是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 楼

如果使用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 楼

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

创建新文件.env

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