Node.js 웹 애플리케이션에서 MongoDB 연결을 어떻게 관리합니까?


288

내가 사용하고 노드 MongoDB를 네이티브 웹 사이트를 작성하여 MongoDB와 드라이버를.

연결 관리 방법에 대한 몇 가지 질문이 있습니다.

  1. 모든 요청에 ​​대해 하나의 MongoDB 연결 만 사용하면 충분합니까? 성능 문제가 있습니까? 그렇지 않은 경우 전체 응용 프로그램에서 사용할 전역 연결을 설정할 수 있습니까?

  2. 그렇지 않은 경우 요청이 도착할 때 새 연결을 열고 요청을 처리 할 때 닫는 것이 좋습니까? 연결을 열고 닫는 데 비용이 많이 듭니까?

  3. 글로벌 연결 풀을 사용해야합니까? 드라이버에 기본 연결 풀이 있다고 들었습니다. 좋은 선택입니까?

  4. 연결 풀을 사용하는 경우 몇 개의 연결을 사용해야합니까?

  5. 주목해야 할 다른 것들이 있습니까?


@ IonicãBizãu, 죄송합니다, 나는 그것을 보지 못했습니다 오랫동안 nodejs를 사용하지 않았습니다. 귀하의 의견에 감사드립니다 ~
Freewind

답변:


459

node-mongodb-native의 주요 커미터는 다음과 같이 말합니다 .

앱이 부팅되고 db 객체를 재사용 할 때 do MongoClient.connect를 한 번 엽니 다. 각 .connect는 단일 연결 풀이 아니므로 새 연결 풀을 만듭니다.

따라서 질문에 직접 대답 하려면의 결과 인 db 객체를 재사용하십시오MongoClient.connect() . 이것은 풀링을 제공하며 각 db 조치에서 연결을 열고 닫는 것과 비교하여 현저한 속도 증가를 제공합니다.



4
이것이 정답입니다. 허용 된 답변은 각 요청에 대해 연결 풀을 연 다음 요청을 닫으면서 매우 잘못되었습니다. 끔찍한 아키텍처.
Saransh Mohapatra

7
정답입니다. 신은 내가 무언가를 할 때마다 열고 닫아야한다고 생각하는데 그것은 단지 삽입물에 대해서만 시간당 350K가 될 것입니다! 내 서버를 공격하는 것과 같습니다.
Maziyar

1
@Cracker : 특급 응용 프로그램 req.db이있는 경우이 미들웨어 를 사용 하여 db 객체를 저장할 수 있습니다 . github.com/floatdrop/express-mongo-db
floatdrop

1
여러 데이터베이스가있는 경우 각 데이터베이스에 대한 연결을 모두 열어야합니다. 그렇지 않은 경우 필요할 때 열고 닫을 수 있습니까?
Aman Gupta

45

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 앱에서 데이터베이스 연결을 여는 방법


1
이것은 하나의 데이터베이스 연결을 생성합니다 ... 풀을 이용하려면 매번 사용 / 생성해야합니다
amcdnl

15
주 제외, 이것은 내가 본 것 중 가장 이상한 NodeJS 파일입니다.
Hobbyist 2016 년

1
이전 app.locals 들어,하지만 난 당신이 여기에 저를 소개 기쁘다 마십시오
Z_z_Z

1
많은 도움이되었습니다! 모든 요청에 ​​대해 DB 연결을 만들거나 닫는 실수를 저지르고 앱의 성능이 떨어졌습니다.
Leandro Lima

18

다음은 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 문서를 기반으로 합니다 . 한번보세요


3
5.x 이후 업데이트 : var option = {numberOfRetries : 5, auto_reconnect : true, poolSize : 40, connectTimeoutMS : 30000};
블레어

15

단일 자체 포함 모듈에서 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()DB 연결이 필요할 때 간단히 호출 할 수 있습니다 .

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

내가 찾던 내용이 매우 유용합니다!
agui

더 나은 방법은 connect () 함수를 제거하고 get () 함수가 연결이 null인지 확인하고 연결이 null인지 확인하십시오. get ()이 항상 약속을 반환하도록하십시오. 이것이 내가 연결을 관리하는 방법이며 훌륭하게 작동합니다. 싱글 톤 패턴을 사용합니다.
java-addict301

@ java-addict301이 접근 방식은보다 간소화 된 API를 제공하지만 두 가지 단점이 있습니다. 첫 번째는 연결 오류를 확인하는 정의 된 방법이 없다는 것입니다. get을 호출 할 때마다 인라인을 처리해야합니다. 데이터베이스 연결이 일찍 실패하고 일반적으로 데이터베이스에 연결하지 않고 앱을 부팅하지 못하게합니다. 다른 문제는 처리량입니다. 활성 연결이 없기 때문에 제어 할 수없는 첫 번째 get () 호출에서 조금 더 기다려야 할 수도 있습니다. 보고 측정 항목이 왜곡 될 수 있습니다.
Stewart

1
@Stewart 응용 프로그램 / 서비스를 구성하는 방법은 일반적으로 시작시 데이터베이스에서 구성을 검색하는 것입니다. 이런 방식으로 데이터베이스에 액세스 할 수없는 경우 응용 프로그램이 시작되지 않습니다. 또한 첫 번째 요청은 항상 시작시이므로이 디자인의 메트릭에는 문제가 없습니다. 또한 연결할 수없는 명확한 오류가있는 싱글 톤에서 한 번에 연결 예외를 다시 발생시킵니다. 어쨌든 연결을 사용할 때 앱에서 데이터베이스 오류를 잡아야하기 때문에 추가 인라인 처리가 발생하지 않습니다.
java-addict301

2
안녕하세요 @Ayan. 여기서 호출 get()할 때 단일 연결이 아닌 연결 풀이 제공 된다는 점에 유의해야 합니다. 이름에서 알 수 있듯이 연결 풀은 논리적 데이터베이스 연결 모음입니다. 풀에 연결이 없으면 드라이버는 연결을 열려고 시도합니다. 해당 연결이 열리면 사용되어 풀로 돌아갑니다. 다음에 풀에 액세스하면이 연결이 재사용 될 수 있습니다. 여기서 좋은 점은 수영장이 우리를 위해 우리의 연결을 관리한다는 것입니다. 연결이 끊어지면 수영장이 우리를 위해 새로운 것을 열 것이라는 것을 알 수 없을 것입니다.
스튜어트

11

Express.js가있는 경우 풀없이 요청간에 MongoDB 연결을 캐싱하고 공유하기 위해 express-mongo-db 를 사용할 수 있습니다 (허용 된 답변이 연결을 공유하는 올바른 방법이라고 말했기 때문에).

그렇지 않은 경우-소스 코드를보고 다른 프레임 워크에서 사용할 수 있습니다.


6

내 앱에서 redis 연결과 함께 일반 풀을 사용하고 있습니다. 적극 권장합니다. 그것의 제네릭과 나는 그것이 mysql과 함께 작동한다는 것을 확실히 알고 있으므로 당신은 그것과 몽고에 아무런 문제가 없을 것이라고 생각하지 않는다.

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


Mongo는 이미 드라이버에서 연결 풀링을 수행하지만 몽고의 경우 정리가 실패하더라도 내 몽고 연결을 노드 풀과 일치하는 인터페이스에 매핑했습니다. 이렇게하면 모든 연결이 동일한 패턴을 따릅니다. 실제로 무엇이든 트리거합니다.
Tracker1

4

서비스로 연결을 만든 다음 필요할 때 다시 사용해야합니다.

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

1
mongo를 연결할 수 없으면 MongoClient.close ()가 오류를 발생시킵니다. 그러나 원래 문제에 대한 좋은 해결책입니다.
Himanshu

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

});

1

연결 풀링을 구현하는 가장 좋은 방법은 MongoClient가 리턴 한 연결 오브젝트로 db 이름을 보유한 전역 배열 변수를 작성하고 데이터베이스에 접속해야 할 때마다 해당 연결을 재사용하는 것입니다.

  1. Server.js에서 var global.dbconnections = [];

  2. 서비스 이름 connectionService.js를 작성하십시오. 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;
} 

0

mongodb.com- > 새 프로젝트-> 새 클러스터-> 새 컬렉션-> 연결-> IP 주소 : 0.0.0.0/0 & db cred-> 응용 프로그램 연결-> 연결 문자열 복사 및 노드의 .env 파일에 붙여 넣기 응용 프로그램과 ""를 사용자의 실제 암호로 바꾸고 "/ test"를 db 이름으로 바꾸십시오.

새 파일 .env 만들기

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

0

Express를 사용하는 경우 Express의 내장 기능을 사용하여 앱 내의 경로와 모듈간에 데이터를 공유하는보다 간단한 방법이 있습니다. app.locals라는 객체가 있습니다. 속성을 첨부하고 경로 내부에서 액세스 할 수 있습니다. 그것을 사용하려면 app.js 파일에서 mongo 연결을 인스턴스화하십시오.

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

이 데이터베이스 연결 또는 실제로 모듈을 통해 공유하려는 다른 모든 데이터는 이제 req.app.locals추가 모듈을 생성하거나 요구할 필요없이 아래와 같이 경로 내에서 액세스 할 수 있습니다 .

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

이 방법을 사용하면 언제든지 닫지 않는 한 앱이 지속되는 동안 데이터베이스 연결을 열 수 있습니다. req.app.locals.your-collection추가 모듈을 사용 하여 쉽게 액세스 할 수 있으며 추가 모듈을 만들 필요가 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.