Come posso gestire le connessioni MongoDB in un'applicazione web Node.js?


288

Sto usando il driver nativo nodo-mongodb con MongoDB per scrivere un sito web.

Ho alcune domande su come gestire le connessioni:

  1. È sufficiente utilizzare una sola connessione MongoDB per tutte le richieste? Ci sono problemi di prestazioni? In caso contrario, posso configurare una connessione globale da utilizzare nell'intera applicazione?

  2. In caso contrario, va bene se apro una nuova connessione quando arriva la richiesta e la chiudo quando gestisco la richiesta? È costoso aprire e chiudere una connessione?

  3. Dovrei usare un pool di connessioni globale? Ho sentito che il driver ha un pool di connessioni native. è una buona scelta?

  4. Se utilizzo un pool di connessioni, quante connessioni devono essere utilizzate?

  5. Ci sono altre cose che dovrei notare?


@ IonicãBizãu, scusa, non uso nodejs da molto tempo che non lo vedo. Grazie per il tuo commento ~
Freewind,

Risposte:


459

Il committer principale del nodo-mongodb-nativo dice :

Apri do MongoClient.connect una volta all'avvio dell'app e riutilizzi l'oggetto db. Non è un pool di connessioni singleton ogni .connect crea un nuovo pool di connessioni.

Quindi, per rispondere direttamente alla tua domanda, riutilizzare l'oggetto db che ne derivaMongoClient.connect() . Questo ti dà il pooling e fornirà un notevole aumento della velocità rispetto alle connessioni di apertura / chiusura su ogni azione db.



4
Questa è la risposta corretta La risposta accettata è molto sbagliata in quanto dice di aprire un pool di connessioni per ogni richiesta e quindi chiuderlo dopo averlo fatto. Architettura terribile.
Saransh Mohapatra,

7
Questa è una risposta giusta Il mio dio immagina di dover aprire e chiudere ogni volta che faccio qualcosa che sarebbe 350K all'ora solo per i miei inserti! È come attaccare il mio server.
Maziyar,

1
@ Cracker: se si dispone di un'applicazione express, è possibile salvare l'oggetto db req.dbcon questo middleware: github.com/floatdrop/express-mongo-db
floatdrop

1
se ci sono più database .. dovrei aprire una connessione per ogni database tutti insieme. In caso contrario, è possibile aprire e chiudere quando richiesto?
Aman Gupta,

45

Aprire una nuova connessione all'avvio dell'applicazione Node.js e riutilizzare l' dboggetto connessione esistente :

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

Fonte: come aprire le connessioni al database in un'app Node.js / Express


1
Questo crea una connessione al database ... se vuoi utilizzare i pool devi creare / chiudere ad ogni utilizzo
amcdnl

15
Fuori tema, questo è il file NodeJS più strano che abbia mai visto.
Hobbyist,

1
Non ho mai sentito parlare di app.locals prima, ma sono felice che tu mi abbia fatto conoscere qui
Z_z_Z,

1
Mi ha aiutato molto! Faccio l'errore di creare / chiudere la connessione DB per ogni richiesta, le prestazioni della mia app sono diminuite con questo.
Leandro Lima,

18

Ecco un po 'di codice che gestirà le tue connessioni 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;

Quando si avvia il server, chiamare initPool

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

Quindi in qualsiasi altro modulo è possibile effettuare le seguenti operazioni:

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

Questo si basa sulla documentazione di MongoDB . Dai un'occhiata.


3
Aggiorna da 5.x: var option = {numberOfRetries: 5, auto_reconnect: true, poolSize: 40, connectTimeoutMS: 30000};
Blair,

15

Gestisci i pool di connessioni mongo in un singolo modulo autonomo. Questo approccio offre due vantaggi. Innanzitutto mantiene il codice modulare e più facile da testare. In secondo luogo, non sei obbligato a confondere la tua connessione al database nell'oggetto richiesta che NON è il posto per un oggetto di connessione al database. (Data la natura di JavaScript, ritengo estremamente pericoloso mescolare qualsiasi cosa a un oggetto costruito dal codice della libreria). Quindi con ciò devi solo considerare un modulo che esporta due metodi. connect = () => Promisee get = () => dbConnectionObject.

Con un tale modulo è innanzitutto possibile connettersi al database

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

Quando sei in volo, la tua app può semplicemente chiamare get()quando necessita di una connessione DB.

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

Se configuri il tuo modulo db nello stesso modo di quanto segue non solo avrai un modo per assicurarti che l'applicazione non si avvii a meno che tu non abbia una connessione al database, hai anche un modo globale di accedere al tuo pool di connessioni al database che se non hai una connessione.

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

molto utile, esattamente quello che stavo cercando!
agui,

Meglio ancora, potresti semplicemente sbarazzarti della funzione connect () e far controllare la funzione get () per vedere se la connessione è nulla e chiamare connect per te se lo è. Get get () restituisce sempre una promessa. Ecco come gestisco la mia connessione e funziona benissimo. Questo è un uso del modello singleton.
java-addict301,

@ java-addict301 Sebbene tale approccio fornisca un'API più snella, presenta due inconvenienti. Il primo è che non esiste un modo definito per verificare la presenza di errori di connessione. Dovresti gestire quell'inline ovunque ovunque tu chiami get. Mi piace fallire presto con le connessioni al database e generalmente non lascerò che l'app si avvii senza una connessione al database. L'altro problema è la velocità effettiva. Poiché non si dispone di una connessione attiva, potrebbe essere necessario attendere un po 'di più alla prima chiamata get () su cui non si avrà il controllo. Potrebbe distorcere le metriche dei rapporti.
Stewart,

1
@Stewart Il modo in cui strutturo le applicazioni / i servizi è in genere recuperare una configurazione dal database all'avvio. In questo modo, l'applicazione non verrà avviata se il database non è accessibile. Inoltre, poiché la prima richiesta è sempre all'avvio, non ci sono problemi con le metriche con questo design. Riscopri anche un'eccezione di connessione con in once place nel singleton con un chiaro errore che non può connettersi. Poiché l'app deve rilevare errori di database quando si utilizza comunque la connessione, ciò non comporta alcuna gestione in linea aggiuntiva.
java-addict301,

2
Ciao @Ayan. È importante notare che qui quando chiamiamo get()stiamo ottenendo un pool di connessioni non una singola connessione. Un pool di connessioni, come suggerisce il nome, è una raccolta logica di connessioni al database. Se non ci sono connessioni nel pool, il driver tenterà di aprirne una. Una volta aperta la connessione, viene utilizzata e restituita al pool. Al successivo accesso al pool questa connessione potrebbe essere riutilizzata. La cosa bella qui è che il pool gestirà le nostre connessioni per noi, quindi se una connessione viene interrotta non potremmo mai sapere come il pool ne aprirà una nuova per noi.
Stewart,

11

Se si dispone di Express.js, è possibile utilizzare express-mongo-db per la memorizzazione nella cache e la condivisione della connessione MongoDB tra richieste senza un pool (poiché la risposta accettata indica che è il modo giusto di condividere la connessione).

Altrimenti, puoi guardare il suo codice sorgente e usarlo in un altro framework.


6

Ho usato il pool generico con connessioni redis nella mia app - lo consiglio vivamente. È generico e sicuramente so che funziona con mysql, quindi non credo che avrai problemi con esso e mongo

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


Mongo esegue già il pool di connessioni nel driver, tuttavia ho mappato le mie connessioni di mongo in un'interfaccia che corrisponde al pool di nodi, in questo modo tutte le mie connessioni seguono lo stesso schema, anche se nel caso di mongo, la pulizia non in realtà innescare qualsiasi cosa.
Tracker1,

4

È necessario creare una connessione come servizio, quindi riutilizzarla quando necessario.

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

il mio esempio 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}`);
  });
});

e usalo dove vuoi con

import dbService from "db.service.js"
const db = dbService.db

1
Se mongo non è riuscito a connettersi, MongoClient.close () dà un errore. Ma una buona soluzione per il problema originale.
Himanshu,

3

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

Scopri la fonte di Mongoose. Aprono una connessione e la associano a un oggetto Modello, quindi quando è necessario l'oggetto Modello, viene stabilita una connessione al DB. Il driver si occupa del pool di connessioni.


È originale per connettore nativo mongodb .
CodeFinity,

2

Ho implementato il codice seguente nel mio progetto per implementare il pool di connessioni nel mio codice in modo da creare una connessione minima nel mio progetto e riutilizzare la connessione disponibile

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

});

1

Il miglior approccio per implementare il pool di connessioni è quello di creare una variabile di array globale che contenga il nome db con l'oggetto di connessione restituito da MongoClient e quindi riutilizzare quella connessione ogni volta che è necessario contattare il database.

  1. Nel tuo Server.js definisci var global.dbconnections = [];

  2. Creare un servizio denominando connectionService.js. Avrà 2 metodi getConnection e createConnection. Quindi, quando l'utente chiamerà getConnection (), troverà i dettagli nella variabile di connessione globale e restituirà i dettagli della connessione se già esiste altrimenti chiamerà createConnection () e restituirà i dettagli della connessione.

  3. Chiamare questo servizio utilizzando db_name e restituirà l'oggetto di connessione se ha già altro, creerà una nuova connessione e te lo restituirà.

Spero che sia d'aiuto :)

Ecco il codice 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;
} 

0

mongodb.com -> nuovo progetto -> nuovo cluster -> nuova collezione -> connetti -> indirizzo IP: 0.0.0.0/0 e credibilità db -> collega la tua applicazione -> copia la stringa di connessione e incolla il file .env del tuo nodo app e assicurati di sostituire "" con la password effettiva per l'utente e di sostituire "/ test" con il tuo nome db

crea un nuovo file .env

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

0

Se si utilizza express, esiste un altro metodo più semplice, che consiste nell'utilizzare la funzione integrata di Express per condividere dati tra route e moduli all'interno della propria app. C'è un oggetto chiamato app.locals. Possiamo associarvi proprietà e accedervi dall'interno dei nostri percorsi. Per usarlo, crea un'istanza della tua connessione mongo nel tuo file app.js.

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

Questa connessione al database o qualsiasi altro dato che desideri condividere attorno ai moduli della tua app ora è ora accessibile all'interno dei tuoi percorsi req.app.localscome di seguito senza la necessità di creare e richiedere moduli aggiuntivi.

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

Questo metodo assicura che tu abbia una connessione al database aperta per la durata della tua app a meno che tu non scelga di chiuderla in qualsiasi momento. È facilmente accessibile con req.app.locals.your-collectione non richiede la creazione di moduli aggiuntivi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.