Bagaimana cara saya mengelola koneksi MongoDB dalam aplikasi web Node.js?


288

Saya menggunakan driver simpul-mongodb-asli dengan MongoDB untuk menulis situs web.

Saya memiliki beberapa pertanyaan tentang cara mengelola koneksi:

  1. Apakah cukup menggunakan hanya satu koneksi MongoDB untuk semua permintaan? Apakah ada masalah kinerja? Jika tidak, dapatkah saya mengatur koneksi global untuk digunakan di seluruh aplikasi?

  2. Jika tidak, apakah baik jika saya membuka koneksi baru ketika permintaan datang, dan menutupnya ketika menangani permintaan? Apakah mahal untuk membuka dan menutup koneksi?

  3. Haruskah saya menggunakan kumpulan koneksi global? Saya mendengar pengemudi memiliki kolam koneksi asli. Apakah ini pilihan yang bagus?

  4. Jika saya menggunakan kumpulan koneksi, berapa banyak koneksi yang harus digunakan?

  5. Apakah ada hal lain yang harus saya perhatikan?


@ IonicãBizãu, maaf, saya sudah lama tidak menggunakan nodejs. Terima kasih atas komentar Anda ~
Freewind

Jawaban:


459

Committer utama ke node-mongodb-native mengatakan :

Anda membuka lakukan MongoClient.connect sekali ketika aplikasi Anda boot dan menggunakan kembali objek db. Ini bukan kumpulan koneksi tunggal .connect membuat kumpulan koneksi baru.

Jadi, untuk menjawab pertanyaan Anda secara langsung, gunakan kembali objek db yang dihasilkanMongoClient.connect() . Ini memberi Anda penggabungan, dan akan memberikan peningkatan kecepatan yang nyata dibandingkan dengan koneksi pembukaan / penutupan pada setiap aksi db.



4
Ini jawaban yang benar. Jawaban yang diterima sangat salah karena dikatakan untuk membuka kumpulan koneksi untuk setiap permintaan dan kemudian menutupnya setelah melakukannya. Arsitektur yang mengerikan.
Saransh Mohapatra

7
Ini jawaban yang tepat. Ya Tuhan, bayangkan aku harus membuka dan menutup setiap kali aku melakukan sesuatu, itu akan menjadi 350 ribu per jam hanya untuk sisipanku! Ini seperti menyerang server saya sendiri.
Maziyar

1
@Cracker: Jika Anda memiliki aplikasi ekspres, Anda dapat menyimpan objek db ke req.dbdalam middleware ini: github.com/floatdrop/express-mongo-db
floatdrop

1
jika ada banyak basis data .. haruskah saya membuka koneksi untuk setiap basis data bersama-sama. Jika tidak, apakah boleh membuka dan menutup jika diperlukan?
Aman Gupta

45

Buka koneksi baru ketika aplikasi Node.js dimulai, dan gunakan kembali dbobjek koneksi yang ada :

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

Sumber: Cara Membuka Koneksi Database di Aplikasi Node.js / Express


1
Ini menciptakan satu koneksi basis data ... jika Anda ingin memanfaatkan kumpulan Anda harus membuat / menutup pada setiap penggunaan
amcdnl

15
Di luar topik, ini adalah file NodeJS teraneh yang pernah saya lihat.
Hobbyist

1
Belum pernah mendengar tentang app.locals sebelumnya, tapi saya senang Anda memperkenalkan saya kepada mereka di sini
Z_z_Z

1
Banyak membantu saya! Saya membuat kesalahan untuk membuat / menutup koneksi DB untuk setiap permintaan, kinerja aplikasi saya turun dengan ini.
Leandro Lima

18

Berikut adalah beberapa kode yang akan mengatur koneksi MongoDB Anda.

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;

Saat Anda memulai server, hubungi initPool

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

Kemudian di modul lain Anda dapat melakukan hal berikut:

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

Ini didasarkan pada dokumentasi MongoDB . Lihatlah itu.


3
Pembaruan sejak 5.x: opsi var = {numberOfRetries: 5, auto_reconnect: true, poolSize: 40, connectTimeoutMS: 30000};
Blair

15

Kelola kumpulan koneksi mongo dalam satu modul mandiri. Pendekatan ini memberikan dua manfaat. Pertama, kode Anda tetap modular dan lebih mudah untuk diuji. Kedua Anda tidak dipaksa untuk mencampur koneksi database Anda di objek permintaan Anda yang BUKAN tempat untuk objek koneksi database. (Mengingat sifat JavaScript, saya akan menganggap sangat berbahaya untuk mencampurkan sesuatu ke objek yang dibangun oleh kode perpustakaan). Maka dengan itu Anda hanya perlu Mempertimbangkan modul yang mengekspor dua metode. connect = () => Promisedan get = () => dbConnectionObject.

Dengan modul seperti itu, Anda dapat terhubung ke database terlebih dahulu

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

Saat dalam penerbangan, aplikasi Anda hanya dapat menelepon get()ketika membutuhkan koneksi DB.

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

Jika Anda mengatur modul db Anda dengan cara yang sama seperti yang berikut ini tidak hanya Anda akan memiliki cara untuk memastikan bahwa aplikasi Anda tidak bisa boot kecuali Anda memiliki koneksi database Anda juga memiliki cara global untuk mengakses kumpulan koneksi database Anda yang akan kesalahan jika Anda belum mendapat koneksi.

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

sangat berguna, persis apa yang saya cari!
agui

Lebih baik lagi, Anda bisa saja menyingkirkan fungsi connect () dan memeriksakan fungsi get () untuk melihat apakah koneksi null dan memanggil connect untuk Anda jika ada. Sudah mendapatkan () selalu mengembalikan janji. Ini adalah bagaimana saya mengatur koneksi saya dan itu berfungsi dengan baik. Ini adalah penggunaan pola singleton.
java-addict301

@ java-addict301 Sementara pendekatan itu memang menyediakan API yang lebih ramping, ia memang memiliki dua kelemahan. Yang pertama adalah bahwa tidak ada cara yang pasti untuk memeriksa kesalahan koneksi. Anda harus menangani inline itu di mana-mana kapan pun Anda menelepon. Saya suka gagal lebih awal dengan koneksi database dan umumnya saya tidak akan membiarkan aplikasi boot tanpa koneksi ke database. Masalah lainnya adalah throughput. Karena Anda tidak memiliki koneksi aktif, Anda mungkin harus menunggu sedikit lebih lama pada panggilan get () pertama yang tidak dapat Anda kendalikan. Dapat membelokkan metrik pelaporan Anda.
Stewart

1
@Stewart Cara saya menyusun aplikasi / layanan adalah dengan biasanya mengambil konfigurasi dari database saat startup. Dengan cara ini, aplikasi akan gagal untuk memulai jika database tidak dapat diakses. Selain itu, karena permintaan pertama selalu pada startup, tidak ada masalah dengan metrik dengan desain ini. Juga lakukan rethrow pengecualian koneksi dengan sekali tempat di singleton dengan kesalahan yang jelas tidak dapat terhubung. Karena aplikasi perlu menangkap kesalahan basis data saat menggunakan koneksi, ini tidak mengarah pada penanganan inline tambahan.
java-addict301

2
Hai @Ayan. Penting untuk dicatat bahwa di sini bahwa ketika kita menelepon get()kita mendapatkan koneksi pool bukan koneksi tunggal. Kumpulan koneksi, sesuai namanya adalah kumpulan logis dari koneksi basis data. Jika tidak ada koneksi di kolam pengemudi akan mencoba untuk membukanya. Setelah koneksi terbuka, digunakan dan kembali ke kolam. Lain kali pool diakses koneksi ini mungkin digunakan kembali. Yang menyenangkan di sini adalah pool akan mengatur koneksi kita untuk kita, jadi jika koneksi terputus kita mungkin tidak akan pernah tahu karena pool akan membuka yang baru untuk kita.
Stewart

11

Jika Anda memiliki Express.js, Anda dapat menggunakan express-mongo-db untuk melakukan caching dan berbagi koneksi MongoDB antara permintaan tanpa kumpulan (karena jawaban yang diterima mengatakan itu adalah cara yang tepat untuk berbagi koneksi).

Jika tidak - Anda dapat melihat kode sumbernya dan menggunakannya dalam kerangka kerja lain.


6

Saya telah menggunakan kolam generik dengan koneksi redis di aplikasi saya - saya sangat merekomendasikannya. Itu generik dan saya pasti tahu itu bekerja dengan mysql jadi saya tidak berpikir Anda akan memiliki masalah dengan itu dan mongo

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


Mongo sudah melakukan koneksi pooling di driver, saya punya, namun memetakan koneksi mongo saya ke antarmuka yang cocok dengan node-pool, dengan cara ini semua koneksi saya mengikuti pola yang sama, meskipun dalam kasus mongo, pembersihan tidak sebenarnya memicu apa pun.
Tracker1

4

Anda harus membuat koneksi sebagai layanan kemudian menggunakannya kembali ketika dibutuhkan.

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

sampel App.js saya

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

dan gunakan di mana pun Anda inginkan

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

1
Jika mongo tidak dapat terhubung, MongoClient.close () memberikan kesalahan. Tetapi solusi yang bagus untuk masalah asli.
Himanshu


2

Saya telah menerapkan kode di bawah ini dalam proyek saya untuk menerapkan pengumpulan koneksi dalam kode saya sehingga akan membuat koneksi minimum dalam proyek saya dan menggunakan kembali koneksi yang tersedia

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

Pendekatan terbaik untuk menerapkan koneksi koneksi adalah Anda harus membuat satu variabel array global yang menyimpan nama db dengan objek koneksi yang dikembalikan oleh MongoClient dan kemudian menggunakan kembali koneksi itu setiap kali Anda perlu menghubungi Database.

  1. Di Server.js Anda tentukan var global.dbconnections = [];

  2. Buat Layanan penamaan connectionService.js. Ini akan memiliki 2 metode getConnection dan createConnection. Jadi ketika pengguna akan memanggil getConnection (), ia akan menemukan detail dalam variabel koneksi global dan mengembalikan detail koneksi jika sudah ada yang lain itu akan memanggil createConnection () dan mengembalikan Detail koneksi.

  3. Panggil layanan ini menggunakan db_name dan itu akan mengembalikan objek koneksi jika sudah ada yang lain itu akan membuat koneksi baru dan mengembalikannya kepada Anda.

Semoga bermanfaat :)

Berikut adalah kode 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 -> proyek baru -> cluster baru -> koleksi baru -> connect -> alamat IP: 0.0.0.0/0 & kredibilitas db -> hubungkan aplikasi Anda -> salin string koneksi dan rekatkan dalam file .env dari node Anda aplikasi dan pastikan untuk mengganti "" dengan kata sandi yang sebenarnya untuk pengguna dan juga mengganti "/ test" dengan nama db Anda

buat file baru .env

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

0

Jika menggunakan express ada metode lain yang lebih mudah, yaitu memanfaatkan fitur bawaan Express untuk berbagi data antara rute dan modul dalam aplikasi Anda. Ada objek bernama app.locals. Kami dapat melampirkan properti padanya dan mengaksesnya dari dalam rute kami. Untuk menggunakannya, instantiate koneksi mongo Anda di file app.js Anda.

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

Koneksi basis data ini, atau memang data lain yang ingin Anda bagikan di sekitar modul aplikasi Anda kini dapat diakses dalam rute Anda dengan req.app.localsseperti di bawah ini tanpa perlu membuat dan membutuhkan modul tambahan.

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

Metode ini memastikan bahwa Anda memiliki koneksi database terbuka selama aplikasi Anda kecuali Anda memilih untuk menutupnya kapan saja. Ini mudah diakses dengan req.app.locals.your-collectiondan tidak memerlukan pembuatan modul tambahan apa pun.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.