MongoDB connection pooling在nodejs应用

在应用中,只要需要连接db的地方,就涉及到,
1,如何取得数据库的连接,
2,取得连接并执行完相应操作后,对此连接如何处理,比如close掉的问题,
** 在nodejs/mongodb的driver中,对于是否要显示的close掉这个connection,有稍微一点不同的思路,
因为mongodb nodejs driver里面实现了一个 poolSize,并且mongodb的官方文档中推荐重用这个通过 poolSize取得的connection ** :
https://mongodb.github.io/node-mongodb-native/driver-articles/mongoclient.html#mongoclient-connection-pooling

MongoClient connection pooling
A Connection Pool is a cache of database connections maintained by the driver so that connections can be re-used when new connections to the database are required. To reduce the number of connection pools created by your application, we recommend calling MongoClient.connect once and reusing the database variable returned by the callback:

var express = require('express');
var mongodb = require('mongodb');
var app = express();

var MongoClient = require('mongodb').MongoClient;
var db;

// Initialize connection once
MongoClient.connect("mongodb://localhost:27017/integration_test", function(err, database) {
  if(err) throw err;

  db = database;

  // Start the application after the database connection is ready
  app.listen(3000);
  console.log("Listening on port 3000");
});

// Reuse database object in request handlers
app.get("/", function(req, res) {
  db.collection("replicaset_mongo_client_collection").find({}, function(err, docs) {
    docs.each(function(err, doc) {
      if(doc) {
        console.log(doc);
      }
      else {
        res.end();
      }
    });
  });
});

上面为官方文档简单写法,下面举例生产环境的一些思路,
https://stackoverflow.com/questions/10656574/how-do-i-manage-mongodb-connections-in-a-node-js-web-application?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa

// 这种方式,值得思考一下是否是比较好的实现方式,
// 这里直接把取得的db连接赋给 app中的变量,app.locals.db = db;如果后续的app应用都需要连接db的话,这样也未尝不可,但是如果后面的app请求或者操作不是都要用到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;

Here is some code that will manage your MongoDB connections. 这里也是重用db连接,每次操作完db后,不用显示的close掉db

var MongoClient = require('mongodb').MongoClient;
var url = require("../config.json")["MongoDBURL"]

var option = {
    reconnectTries: 3,
    auto_reconnect: true,
    poolSize : 40,
    connectTimeoutMS: 500
};

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;

When you start the server, call initPool

require("mongo-pool").initPool();

Then in any other module you can do the following:

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

还有一种方式,类似上面的方法,只是把取得的db连接和db的名字存入到global.dbconnections的全局变量里,具体实现参考如下

Best approach to implement connection pooling is you should create one global array variable which hold db name with connection object returned by MongoClient and then reuse that connection whenever you need to contact Database.
In your Server.js define var global.dbconnections = [];
Create a Service naming connectionService.js. It will have 2 methods getConnection and createConnection. So when user will call getConnection(), it will find detail in global connection variable and return connection details if already exists else it will call createConnection() and return connection Details.
Call this service using db_name and it will return connection object if it already have else it will create new connection and return it to you.
Hope it helps :)
Here is the connectionService.js code:

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

这里讲解了为什么poolSize很重要,并且不要每个请求都创建一个connection,MongoDB connection pooling for Express applications
https://blog.mlab.com/2017/05/mongodb-connection-pooling-for-express-applications/
What is a connection pool and why is it important?

What is a connection pool and why is it important?
A connection pool is a cache of authenticated database connections maintained by your driver, from which your application can borrow connections when it needs to run database operations. After each operation is complete, the connection is kept alive and returned to the pool. When properly used, connection pools allow you to minimize the frequency of new connections and the number of open connections to your database.
It’s important to minimize the frequency and number of new connections to the database because creating and authenticating connections to the database is expensive - these processes require both CPU time and memory. When we reuse connections by using a connection pool we avoid this resource cost to the server, and also benefit from lower latency since the app doesn’t need to wait for an authentication to finish before a query can be sent.
The default pool size in the Node driver is 5. If your app typically needs more than 5 concurrent connections to the database, you can increase the value of the poolSize setting.
Common mistake in connection management for Express apps
A common mistake developers make when connecting to the database is to call MongoClient.connect() in every route handler to get a database connection.
For example, code like this:

app.route('/foo', (req, res) => {
  MongoClient.connect('url', (err, client) => {
    ...
  })
})

app.route('/bar', (req, res) => {
  MongoClient.connect('url', (err, client) => {
    ...
  })
})

will create 5 connections to MongoDB for every request, including concurrent requests to the same route. Connection counts can quickly skyrocket with this approach

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值