Làm cách nào để quản lý các kết nối MongoDB trong ứng dụng web Node.js?


288

Tôi đang sử dụng trình điều khiển gốc nút-mongodb với MongoDB để viết một trang web.

Tôi có một số câu hỏi về cách quản lý kết nối:

  1. Có đủ chỉ bằng một kết nối MongoDB cho tất cả các yêu cầu không? Có bất kỳ vấn đề hiệu suất? Nếu không, tôi có thể thiết lập kết nối toàn cầu để sử dụng trong toàn bộ ứng dụng không?

  2. Nếu không, có tốt không nếu tôi mở một kết nối mới khi có yêu cầu đến và đóng nó khi xử lý yêu cầu? Có tốn kém để mở và đóng một kết nối?

  3. Tôi có nên sử dụng nhóm kết nối toàn cầu? Tôi nghe thấy trình điều khiển có một hồ kết nối bản địa. Nó có phải là một lựa chọn tốt?

  4. Nếu tôi sử dụng nhóm kết nối, nên sử dụng bao nhiêu kết nối?

  5. Có những điều khác tôi nên chú ý?


@ IonicãBizãu, xin lỗi, tôi đã không sử dụng nodejs trong một thời gian dài mà tôi không thấy nó. Cảm ơn bình luận của bạn ~
Freewind

Câu trả lời:


459

Committer chính cho nút-mongodb -igen nói :

Bạn mở do MongoClient.connect một lần khi ứng dụng của bạn khởi động và sử dụng lại đối tượng db. Đây không phải là nhóm kết nối đơn, mỗi .connect tạo ra một nhóm kết nối mới.

Vì vậy, để trả lời trực tiếp câu hỏi của bạn, hãy sử dụng lại đối tượng db có kết quảMongoClient.connect() . Điều này cung cấp cho bạn nhóm và sẽ cung cấp một sự gia tăng tốc độ đáng chú ý so với các kết nối mở / đóng trên mỗi hành động db.


3
Liên kết đến MongoClient.connect () mongodb.github.io/node-mongodb-native/do-articles/ Kẻ
AndrewJM

4
Đây là câu trả lời chính xác. Câu trả lời được chấp nhận là rất sai vì nó nói để mở một nhóm kết nối cho mỗi yêu cầu và sau đó đóng nó sau khi thực hiện. Kiến trúc khủng khiếp.
Saransh Mohapatra

7
Đây là một câu trả lời đúng. Chúa ơi hãy tưởng tượng rằng tôi phải mở và đóng mỗi lần tôi làm một việc gì đó sẽ là 350K mỗi giờ chỉ cho những lần chèn của tôi! Nó giống như tấn công máy chủ của riêng tôi.
Maziyar

1
@Cracker: Nếu bạn có ứng dụng express, bạn có thể lưu đối tượng db vào req.dbvới phần mềm trung gian này: github.com/floatdrop/express-mongo-db
floatdrop

1
nếu có nhiều cơ sở dữ liệu .. tôi có nên mở một kết nối cho mỗi cơ sở dữ liệu cùng nhau không. Nếu không, có thể mở và đóng khi cần thiết không?
Aman Gupta

45

Mở một kết nối mới khi ứng dụng Node.js khởi động và sử dụng lại kết nối hiện có db đối tượng kết nối :

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

Nguồn: Cách mở kết nối cơ sở dữ liệu trong ứng dụng Node.js / Express


1
Điều này tạo ra một kết nối cơ sở dữ liệu ... nếu bạn muốn sử dụng các nhóm bạn phải tạo / đóng mỗi lần sử dụng
amcdnl

15
Ngoài chủ đề, đây là tệp NodeJS kỳ lạ nhất tôi từng thấy.
sở thích

1
Chưa bao giờ nghe nói về app.locals trước đây, nhưng tôi rất vui vì bạn đã giới thiệu tôi với họ ở đây
Z_z_Z

1
Đã giúp tôi rất nhiều! Tôi đã phạm sai lầm khi tạo / đóng kết nối DB cho mọi yêu cầu, sự hoàn hảo của ứng dụng của tôi đã giảm xuống với điều này.
Leandro Lima

18

Đây là một số mã sẽ quản lý các kết nối MongoDB của bạn.

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;

Khi bạn khởi động máy chủ, hãy gọi initPool

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

Sau đó, trong bất kỳ mô-đun khác, bạn có thể làm như sau:

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

Điều này dựa trên tài liệu MongoDB . Hãy nhìn vào nó.


3
Cập nhật kể từ 5.x: var tùy chọn = {numberOfRetries: 5, auto_reconnect: true, poolSize: 40, connectTimeoutMS: 30000};
Blair

15

Quản lý nhóm kết nối mongo trong một mô-đun độc lập. Cách tiếp cận này cung cấp hai lợi ích. Đầu tiên, nó giữ cho mã của bạn được mô-đun và dễ kiểm tra hơn. Thứ hai, bạn không bị buộc phải trộn kết nối cơ sở dữ liệu của bạn trong đối tượng yêu cầu của bạn KHÔNG phải là nơi dành cho đối tượng kết nối cơ sở dữ liệu. (Với bản chất của JavaScript, tôi sẽ cho rằng rất nguy hiểm khi trộn lẫn bất cứ thứ gì vào một đối tượng được xây dựng bằng mã thư viện). Vì vậy, với điều đó bạn chỉ cần xem xét một mô-đun xuất hai phương thức. connect = () => Promiseget = () => dbConnectionObject.

Với một mô-đun như vậy, trước tiên bạn có thể kết nối với cơ sở dữ liệu

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

Khi đang bay, ứng dụng của bạn chỉ cần gọi get()khi cần kết nối DB.

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

Nếu bạn thiết lập mô-đun db của mình theo cách tương tự như sau, bạn sẽ không có cách nào để đảm bảo rằng ứng dụng của bạn sẽ không khởi động trừ khi bạn có kết nối cơ sở dữ liệu, bạn cũng có cách truy cập nhóm kết nối cơ sở dữ liệu của mình. nếu bạn không có kết nối.

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

rất hữu ích, chính xác những gì tôi đang tìm kiếm!
agui

Tốt hơn nữa, bạn có thể thoát khỏi hàm connect () và kiểm tra hàm get () để xem kết nối có rỗng không và gọi kết nối cho bạn nếu có. Có được () luôn trả lại một lời hứa. Đây là cách tôi quản lý kết nối của mình và nó hoạt động rất tốt. Đây là một cách sử dụng mô hình singleton.
java-nghiện602

@ java-nghiện602 Mặc dù cách tiếp cận đó cung cấp API hợp lý hơn nhưng nó có hai nhược điểm. Đầu tiên là không có cách xác định để kiểm tra lỗi kết nối. Bạn sẽ phải xử lý nội tuyến đó ở mọi nơi khi bạn gọi get. Tôi muốn thất bại sớm với các kết nối cơ sở dữ liệu và nói chung tôi sẽ không để ứng dụng khởi động mà không kết nối với cơ sở dữ liệu. Vấn đề khác là thông lượng. Vì bạn không có kết nối hoạt động, bạn có thể phải chờ thêm một chút trong cuộc gọi get () đầu tiên mà bạn sẽ không có quyền kiểm soát. Có thể sai lệch số liệu báo cáo của bạn.
Stewart

1
@Stewart Cách tôi cấu trúc các ứng dụng / dịch vụ là thường lấy một cấu hình từ cơ sở dữ liệu khi khởi động. Theo cách này, ứng dụng sẽ không khởi động được nếu cơ sở dữ liệu không thể truy cập được. Ngoài ra, vì yêu cầu đầu tiên luôn được khởi động, nên không có vấn đề gì với các số liệu với thiết kế này. Ngoài ra, hãy thiết lập lại một ngoại lệ kết nối với một lần ở vị trí đơn lẻ với một lỗi rõ ràng mà nó không thể kết nối. Vì ứng dụng cần bắt lỗi cơ sở dữ liệu khi sử dụng kết nối, nên điều này không dẫn đến bất kỳ xử lý nội tuyến bổ sung nào.
java-nghiện602

2
Xin chào @Ayan. Điều quan trọng cần lưu ý là ở đây, khi chúng tôi gọi, get()chúng tôi sẽ nhận được một nhóm kết nối chứ không phải là một kết nối duy nhất. Nhóm kết nối, như tên của nó ngụ ý là một tập hợp logic các kết nối cơ sở dữ liệu. Nếu không có kết nối trong nhóm, trình điều khiển sẽ cố gắng mở một kết nối. Khi kết nối đó được mở, nó được sử dụng và quay trở lại nhóm. Lần tiếp theo hồ bơi được truy cập kết nối này có thể được sử dụng lại. Điều tuyệt vời ở đây là pool sẽ quản lý các kết nối của chúng tôi cho chúng tôi vì vậy nếu kết nối bị ngắt, chúng tôi có thể không bao giờ biết vì pool sẽ mở một kết nối mới cho chúng tôi.
Stewart

11

Nếu bạn có Express.js, bạn có thể sử dụng express-mongo-db để lưu trữ và chia sẻ kết nối MongoDB giữa các yêu cầu mà không có nhóm (vì câu trả lời được chấp nhận cho biết đây là cách phù hợp để chia sẻ kết nối).

Nếu không - bạn có thể xem mã nguồn của nó và sử dụng nó trong một khung khác.


6

Tôi đã và đang sử dụng pool chung với các kết nối redis trong ứng dụng của mình - tôi thực sự khuyên bạn nên dùng nó. Nó chung chung và tôi chắc chắn biết rằng nó hoạt động với mysql vì vậy tôi không nghĩ rằng bạn sẽ có bất kỳ vấn đề nào với nó và mongo

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


Mongo đã thực hiện tổng hợp kết nối trong trình điều khiển, tuy nhiên, tôi đã ánh xạ các kết nối mongo của mình vào một giao diện khớp với nhóm nút, theo cách này, tất cả các kết nối của tôi đều theo cùng một mẫu, mặc dù trong trường hợp của mongo, việc dọn dẹp không thực sự kích hoạt bất cứ điều gì.
Tracker1

4

Bạn nên tạo một kết nối như dịch vụ sau đó sử dụng lại khi cần.

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

mẫu App.js của tôi

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

và sử dụng nó bất cứ nơi nào bạn muốn với

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

1
Nếu mongo không thể kết nối, MongoClient.close () sẽ báo lỗi. Nhưng một giải pháp tốt cho vấn đề ban đầu.
Himanshu

3

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

Kiểm tra nguồn của Mongoose. Họ mở một kết nối và liên kết nó với một đối tượng Model để khi đối tượng mô hình được yêu cầu, một kết nối được tạo ra cho DB. Trình điều khiển chăm sóc kết nối tổng hợp.


Bản gốc cho kết nối gốc mongodb .
CodeFinity

2

Tôi đã triển khai mã bên dưới trong dự án của mình để triển khai nhóm kết nối trong mã của mình để nó sẽ tạo kết nối tối thiểu trong dự án của tôi và sử dụng lại kết nối có sẵn

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

Cách tiếp cận tốt nhất để thực hiện nhóm kết nối là bạn nên tạo một biến mảng toàn cục chứa tên db với đối tượng kết nối được MongoClient trả về và sau đó sử dụng lại kết nối đó bất cứ khi nào bạn cần liên hệ với Cơ sở dữ liệu.

  1. Trong Server.js của bạn, hãy xác định var global.dbconnections = [];

  2. Tạo một dịch vụ đặt tên kết nốiService.js. Nó sẽ có 2 phương thức getConnection và createdConnection. Vì vậy, khi người dùng sẽ gọi getConnection (), nó sẽ tìm thấy chi tiết trong biến kết nối toàn cầu và trả về chi tiết kết nối nếu đã tồn tại, nó sẽ gọi createConnection () và trả về Chi tiết kết nối.

  3. Gọi dịch vụ này bằng db_name và nó sẽ trả về đối tượng kết nối nếu nó đã có khác, nó sẽ tạo kết nối mới và trả lại cho bạn.

Hy vọng nó giúp :)

Đây là mã 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 -> dự án mới -> cụm mới -> bộ sưu tập mới -> kết nối -> địa chỉ IP: 0.0.0.0/0 & db credit -> kết nối ứng dụng của bạn -> sao chép chuỗi kết nối và dán vào tệp .env của nút của bạn ứng dụng và đảm bảo thay thế "" bằng mật khẩu thực tế cho người dùng và cũng thay thế "/ test" bằng tên db của bạn

tạo tập tin mới .env

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

0

Nếu sử dụng express, có một phương pháp khác đơn giản hơn, đó là sử dụng tính năng tích hợp sẵn của Express để chia sẻ dữ liệu giữa các tuyến và mô-đun trong ứng dụng của bạn. Có một đối tượng được gọi là app.locals. Chúng tôi có thể đính kèm các thuộc tính cho nó và truy cập nó từ bên trong các tuyến đường của chúng tôi. Để sử dụng nó, hãy khởi tạo kết nối mongo của bạn trong tệp app.js của bạn.

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

Kết nối cơ sở dữ liệu này, hoặc thực sự là bất kỳ dữ liệu nào bạn muốn chia sẻ xung quanh các mô-đun của ứng dụng của bạn giờ đây có thể được truy cập trong các tuyến đường của bạn req.app.localsnhư dưới đây mà không cần phải tạo và yêu cầu các mô-đun bổ sung.

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

Phương pháp này đảm bảo rằng bạn có kết nối cơ sở dữ liệu mở trong suốt thời gian ứng dụng của bạn trừ khi bạn chọn đóng nó bất cứ lúc nào. Nó dễ dàng truy cập req.app.locals.your-collectionvà không yêu cầu tạo bất kỳ mô-đun bổ sung nào.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.