Как управлять соединениями MongoDB в веб-приложении Node.js?

Я использую драйвер node-mongodb-native с MongoDB для создания веб-сайта.

У меня есть некоторые вопросы о том, как управлять соединениями:

  • Достаточно ли использовать только одно соединение MongoDB для всех запросов? Есть ли проблемы с производительностью? Если нет, могу ли я настроить глобальное соединение для использования во всем приложении?

  • Если нет, хорошо ли открыть новое соединение при поступлении запроса и закрыть его при обработке запроса? Стоит ли открывать и закрывать соединение дорого?

  • Должен ли я использовать глобальный пул соединений? Я слышал, что у драйвера есть собственный пул соединений. Это хороший выбор?

  • Если я использую пул соединений, сколько соединений должно использоваться?

  • Есть ли другие вещи, которые я должен заметить?

Ответ 1

Основной коммиттер node -mongodb-native говорит:

Вы открываете make MongoClient.connect один раз, когда ваше приложение загружается и повторно используется объект db. Это не единичный пул соединений. создает новый пул соединений.

Итак, чтобы напрямую ответить на ваш вопрос, повторно используйте объект db, полученный в результате MongoClient.connect(). Это дает вам объединение и обеспечивает заметное увеличение скорости по сравнению с открытием/закрытием соединений при каждом действии db.

Ответ 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 приложении

Ответ 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. Посмотрите на это.

Ответ 4

Если у вас есть Express.js, вы можете использовать express-mongo-db для кэширования и совместного использования соединения MongoDB между запросами без пула (поскольку в принятом ответе говорится, что это правильный способ разделить соединение).

Если нет - вы можете посмотреть его исходный код и использовать его в другой структуре.

Ответ 5

Управляйте пулами соединений 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

Если вы настроили свой модуль db таким же образом, как и следующий, вы не сможете гарантировать, что ваше приложение не будет загружаться, если у вас нет подключения к базе данных, у вас также есть глобальный способ доступа к пулу подключений к базе данных это приведет к ошибке, если у вас нет соединения.

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

Ответ 6

Я использую общий пул с подключениями redis в своем приложении - я очень рекомендую его. Его общий и я определенно знаю, что он работает с mysql, поэтому я не думаю, что у вас будут проблемы с ним и mongo

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

Ответ 7

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

Проверьте источник Mongoose. Они открывают соединение и привязывают его к объекту модели, поэтому, когда требуется объект модели, в DB создается соединение. Водитель заботится о пуле соединений.

Ответ 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

Вы должны создать соединение как сервис, а затем использовать его, когда это необходимо.

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

Ответ 10

Наилучший подход для реализации пула соединений заключается в том, что вы должны создать одну глобальную переменную массива, которая будет содержать имя базы данных с объектом соединения, возвращенным MongoClient, а затем повторно использовать это соединение всякий раз, когда вам нужно связаться с базой данных.

  1. В вашем Server.js определите var global.dbconnections = [];

  2. Создайте сервис именования connectionService.js. У него будет 2 метода getConnection и createConnection. Поэтому, когда пользователь вызовет getConnection(), он найдет детали в глобальной переменной соединения и вернет детали соединения, если уже существует, иначе он вызовет createConnection() и вернет детали соединения.

  3. Вызовите этот сервис, используя 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;
} 

Ответ 11

мы можем создать файл dbconnection, такой как dbconnection.js

const MongoClient = require('mongodb').MongoClient
const mongo_url = process.env.MONGO_URL;

    module.exports = {
        connect: async function(callback) {
            var connection;
            await new Promise((resolve, reject) => {
                MongoClient.connect(mongo_url, {
                    useNewUrlParser: true
                }, (err, database) => {
                    if (err)
                        reject();
                    else {
                        connection = database;
                        resolve();
                    }
                });
            });
            return connection;
        }

    };

а затем использовать этот файл в вашем приложении, как

var connection = require('../dbconnection');

а затем использовать как это внутри вашей асинхронной функции

db  = await connection.connect();

надеюсь это сработает