簡體   English   中英

如何在 Node.js web 應用程序中管理 MongoDB 連接?

[英]How do I manage MongoDB connections in a Node.js web application?

我正在使用帶有 MongoDB 的node-mongodb-native驅動程序來編寫一個網站。

我對如何管理連接有一些疑問:

  1. 對所有請求僅使用一個 MongoDB 連接是否足夠? 有任何性能問題嗎? 如果沒有,我可以設置一個全局連接以在整個應用程序中使用嗎?

  2. 如果不是,當請求到達時打開一個新連接,並在處理請求時關閉它是否好? 打開和關閉連接的開銷大嗎?

  3. 我應該使用全局連接池嗎? 我聽說驅動程序有一個本地連接池。 這是一個好的選擇嗎?

  4. 如果我使用連接池,應該使用多少個連接?

  5. 還有其他我應該注意的事情嗎?

node-mongodb-native 的主要提交者說

當您的應用程序啟動並重用 db 對象時,您打開 do MongoClient.connect 一次。 它不是一個單獨的連接池,每個 .connect 都會創建一個新的連接池。

因此,要直接回答您的問題,請重用MongoClient.connect()產生的 db 對象 這為您提供了池化,並且與在每個 db 操作上打開/關閉連接相比,將顯着提高速度。

在 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 應用程序中打開數據庫連接

下面是一些將管理您的 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 文檔 看一看。

在單個自包含模塊中管理 mongo 連接池。 這種方法有兩個好處。 首先,它使您的代碼保持模塊化並且更易於測試。 其次,您不必在請求對象中混合數據庫連接,這不是數據庫連接對象的位置。 (鑒於 JavaScript 的性質,我認為將任何內容混合到由庫代碼構造的對象中是非常危險的)。 因此,您只需要考慮一個導出兩種方法的模塊。 connect = () => Promiseget = () => 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

如果您以與以下相同的方式設置 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;
}

如果你有 Express.js,你可以使用express-mongo-db在沒有池的請求之間緩存和共享 MongoDB 連接(因為接受的答案說這是共享連接的正確方法)。

如果沒有 - 您可以查看其源代碼並在另一個框架中使用它。

您應該創建一個連接作為服務,然后在需要時重用它。

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

我一直在我的應用程序中使用帶有 redis 連接的通用池 - 我強烈推薦它。 它是通用的,我絕對知道它適用於 mysql 所以我認為你不會對它和 mongo 有任何問題

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

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

查看貓鼬的來源。 他們打開一個連接並將其綁定到Model對象,因此當需要Model Object時,將與數據庫建立連接。 驅動程序負責連接池。

我在我的項目中實現了以下代碼以在我的代碼中實現連接池,因此它將在我的項目中創建最小連接並重用可用連接

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

});

實現連接池的最佳方法是創建一個全局數組變量,該變量保存數據庫名稱和MongoClient返回的連接對象,然后在需要聯系數據庫時重用該連接。

  1. 在你的Server.js定義var global.dbconnections = [];

  2. 創建一個名為connectionService.js的服務。 它將有 2 個方法getConnectioncreateConnection 因此,當用戶調用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;
} 

如果使用 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輕松訪問,並且不需要創建任何其他模塊。

如果有人想要在 2021 年使用 Typescript 的東西,這就是我正在使用的:

import { MongoClient, Collection } from "mongodb";

const FILE_DB_HOST = process.env.FILE_DB_HOST as string;
const FILE_DB_DATABASE = process.env.FILE_DB_DATABASE as string;
const FILES_COLLECTION = process.env.FILES_COLLECTION as string;

if (!FILE_DB_HOST || !FILE_DB_DATABASE || !FILES_COLLECTION) {
  throw "Missing FILE_DB_HOST, FILE_DB_DATABASE, or FILES_COLLECTION environment variables.";
}

const client = new MongoClient(FILE_DB_HOST, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

class Mongoose {
  static FilesCollection: Collection;

  static async init() {
    const connection = await client.connect();
    const FileDB = connection.db(FILE_DB_DATABASE);
    Mongoose.FilesCollection = FileDB.collection(FILES_COLLECTION);
  }
}


Mongoose.init();

export default Mongoose;

我相信如果請求發生得太快(在Mongo.init()有時間完成之前),將會拋出錯誤,因為Mongoose.FilesCollection將是未定義的。

import { Request, Response, NextFunction } from "express";
import Mongoose from "../../mongoose";

export default async function GetFile(req: Request, res: Response, next: NextFunction) {
  const files = Mongoose.FilesCollection;
  const file = await files.findOne({ fileName: "hello" });
  res.send(file);
}

例如,如果您調用files.findOne({ ... })並且Mongoose.FilesCollection未定義,那么您將收到錯誤消息。

使用以下方法,您可以輕松管理盡可能多的連接

var mongoose = require('mongoose');


//Set up default mongoose connection
const bankDB = ()=>{
    return  mongoose.createConnection('mongodb+srv://<username>:<passwprd>@mydemo.jk4nr.mongodb.net/<database>?retryWrites=true&w=majority',options);
    
}

bankDB().then(()=>console.log('Connected to mongoDB-Atlas bankApp...'))
       .catch((err)=>console.error('Could not connected to mongoDB',err));
       
//Set up second mongoose connection
const myDB = ()=>{
    return  mongoose.createConnection('mongodb+srv://<username>:<password>@mydemo.jk4nr.mongodb.net/<database>?retryWrites=true&w=majority',options);
   
}
myDB().then(()=>console.log('Connected to mongoDB-Atlas connection 2...'))
       .catch((err)=>console.error('Could not connected to mongoDB',err));

module.exports = { bankDB(), myDB() };

npm i express mongoose

mongodb.js

const express = require('express');
const mongoose =require('mongoose')
const app = express();

mongoose.set('strictQuery', true);
mongoose.connect('mongodb://localhost:27017/db_name', {
    useNewUrlParser: true, 
    useUnifiedTopology: true
})
.then(() => console.log('MongoDB Connected...'))
.catch((err) => console.log(err))

app.listen(3000,()=>{ console.log("Started on port 3000 !!!") })
node mongodb.js

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM