Вопрос по node.js, mongodb – Как мне управлять подключениями MongoDB в веб-приложении Node.js?

231

Я используюУзел-MongoDB-родной драйвер с MongoDB, чтобы написать сайт.

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

Is it enough using only one MongoDB connection for all requests? Are there any performance issues? If not, can I setup a global connection to use in the whole application?

If not, is it good if I open a new connection when request arrives, and close it when handled the request? Is it expensive to open and close a connection?

Should I use a global connection pool? I hear the driver has a native connection pool. Is it a good choice?

If I use a connection pool, how many connections should be used?

Are there other things I should notice?

Почему вы не принимаетеthis answer? Ionică Bizău
@ Ionic; Biz & # xE3; у вас, извините, я не использую nodejs в течение долгого времени, которого я не видел. Спасибо за ваш комментарий ~ Freewind

Ваш Ответ

10   ответов
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
Если mongo не может подключиться, MongoClient.close () выдает ошибку. Но хорошее решение для оригинальной проблемы.
35

Откройте новое соединение при запуске приложения 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;

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

Никогда раньше не слышал о app.locals, но я рад, что вы познакомили меня с ними здесь
Это создает одно соединение с базой данных ... если вы хотите использовать пулы, вы должны создавать / закрывать при каждом использовании
Не по теме, это самый странный файл NodeJS, который я когда-либо видел.
Мне очень помогли! Я делаю ошибку, чтобы создать / закрыть соединение с БД для каждого запроса, производительность моего приложения упала с этим.
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 документация, Посмотрите на это.

Обновление с 5.x: var option = {numberOfRetries: 5, auto_reconnect: true, poolSize: 40, connectTimeoutMS: 30000};
11

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

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

6

Я использовал generic-pool с подключениями redis в своем приложении - я очень рекомендую его. Он универсален, и я точно знаю, что он работает с mysql, поэтому я не думаю, что у вас возникнут какие-либо проблемы с ним и Монго.

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

Mongo уже выполняет пул соединений в драйвере, однако я сопоставил мои монго-соединения с интерфейсом, который соответствует пулу узлов, таким образом, все мои соединения следуют одному и тому же шаблону, даже если в случае с монго очистка не выполняется. на самом деле вызвать что-нибудь.
400

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

You open do MongoClient.connect once when your app boots up and reuse the db object. It's not a singleton connection pool each .connect creates a new connection pool.

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

Должен быть помечен как правильный ответ.
Это правильный ответ. Боже мой, представьте, что мне приходится открывать и закрывать каждый раз, когда я что-то делаю, это будет 350K в час только для моих вкладышей! Это похоже на атаку на мой собственный сервер.
Это правильный ответ. Принятый ответ очень неправильный, так как он говорит, что нужно открывать пул соединений для каждого запроса, а затем закрывать его после этого. Ужасная архитектура.
Ссылка на MongoClient.connect ()mongodb.github.io/node-mongodb-native/driver-articles/…
@Cracker: Если у вас есть экспресс-приложение, вы можете сохранить объект БД вreq.db с этим промежуточным ПО:github.com/floatdrop/express-mongo-db
9

Управляйте пулами соединений Монго в одном автономном модуле. Этот подход обеспечивает два преимущества. Во-первых, это делает ваш код модульным и его легче тестировать. Во-вторых, вам не нужно смешивать соединение с базой данных в объекте запроса, который НЕ является местом для объекта соединения с базой данных. (Учитывая природу 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

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

// 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;
}
@ Stewart Как бы вы справились со случаем, когда монго-соединение умирает на более поздней стадии после соединения? В этом сценарии все вызовы get () не будут выполнены, пока приложение узла не будет перезапущено
Более того, вы можете просто избавиться от функции connect () и проверить функцию get (), чтобы увидеть, является ли соединение пустым, и вызвать connect для вас, если оно есть. У get () всегда возвращаются обещания. Так я управляю своим соединением, и оно прекрасно работает. Это использование шаблона синглтона.
@Stewart Способ, которым я структурирую приложения / сервисы, обычно заключается в получении конфигурации из базы данных при запуске. Таким образом, приложение не запустится, если база данных недоступна. Кроме того, поскольку первый запрос всегда выполняется при запуске, нет проблем с показателями с таким дизайном. Также повторно сгенерируйте исключение соединения с одним местом в синглтоне с явной ошибкой, которую он не может соединить. Так как приложению все равно нужно перехватывать ошибки базы данных при использовании соединения, это не приводит к какой-либо дополнительной встроенной обработке.
@ java-addict301 Хотя этот подход обеспечивает более упорядоченный API, у него есть два недостатка. Во-первых, нет определенного способа проверить наличие ошибок подключения. Вы должны были бы обрабатывать эту строку везде, где бы вы ни звонили. Мне нравится рано выходить из строя с соединениями с базой данных и обычно я не позволяю приложению загружаться без соединения с базой данных. Другая проблема - пропускная способность. Поскольку у вас нет активного соединения, вам, возможно, придется подождать немного дольше при первом вызове get (), который вы не сможете контролировать. Может исказить ваши показатели отчетности.
очень полезно, именно то, что я искал!
1

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

  1. In your Server.js define var global.dbconnections = [];

  2. 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.

  3. 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.

Надеюсь, поможет :)

Вот код 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;
} 
3

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

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

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

});

Похожие вопросы