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

Fre*_*ind 260 database-connection connection-pooling mongodb node.js

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

我有一些关于如何管理连接的问题:

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

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

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

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

  5. 还有其他我应该注意的事情吗?

Max*_*Max 436

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

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

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

  • 这是一个正确的答案.我的上帝想象每次我做的事情都必须打开和关闭,我的插入物每小时只需350K!这就像攻击我自己的服务器. (7认同)
  • 这是正确的答案.接受的答案非常错误,因为它表示为每个请求打开一个连接池,然后在执行此操作后将其关闭.可怕的建筑. (4认同)
  • 链接到MongoClient.connect()http://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html#mongoclient-connect (3认同)
  • 对于最新的 SDK,这个答案仍然正确吗?现在 connect 返回一个客户端(而不是数据库)。我们还应该假设同样的模式吗?只调用一次connect,只调用一次close? (3认同)
  • 你什么时候关闭连接?除了停止应用程序服务器之外,为什么还要关闭它?如果您停止应用程序服务器,连接是否会自动关闭? (2认同)

Kon*_*kus 40

在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}`);
  });
});
Run Code Online (Sandbox Code Playgroud)

/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;
Run Code Online (Sandbox Code Playgroud)

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

  • 偏离主题,这是我见过的最奇怪的NodeJS文件. (14认同)
  • 以前从未听说过 app.locals,但很高兴你在这里向我介绍了他们 (3认同)
  • 对我帮助很大!我错误地为每个请求创建/关闭数据库连接,我的应用程序的性能因此下降。 (2认同)

Yak*_*ein 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;
Run Code Online (Sandbox Code Playgroud)

启动服务器时,请致电 initPool

require("mongo-pool").initPool();
Run Code Online (Sandbox Code Playgroud)

然后在任何其他模块中,您可以执行以下操作:

var MongoPool = require("mongo-pool");
MongoPool.getInstance(function (db){
    // Query your MongoDB database.
});
Run Code Online (Sandbox Code Playgroud)

这基于MongoDB文档.看看吧.

  • 自5.x以来更新:var option = {numberOfRetries:5,auto_reconnect:true,poolSize:40,connectTimeoutMS:30000}; (3认同)

Ste*_*art 13

在单个自包含模块中管理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);
    });
Run Code Online (Sandbox Code Playgroud)

在飞行中,您的应用程序可以get()在需要数据库连接时调用.

const db = require('./myAwesomeDbModule');
db.get().find(...)... // I have excluded code here to keep the example  simple
Run Code Online (Sandbox Code Playgroud)

如果以与以下相同的方式设置数据库模块,除了有数据库连接之外,您还有办法确保应用程序无法启动,您还可以使用全局方式访问数据库连接池,这将导致错误如果你没有连接.

// 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;
}
Run Code Online (Sandbox Code Playgroud)

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

flo*_*rop 11

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

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


小智 8

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

// 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;
Run Code Online (Sandbox Code Playgroud)

我的 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}`);
  });
});
Run Code Online (Sandbox Code Playgroud)

并随心所欲地使用它

import dbService from "db.service.js"
const db = dbService.db
Run Code Online (Sandbox Code Playgroud)


Con*_*Del 6

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

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