【问题标题】:What is best way to handle global connection of Mongodb in NodeJs在 NodeJs 中处理 Mongodb 全局连接的最佳方法是什么
【发布时间】:2021-11-28 12:00:45
【问题描述】:

我使用Node-Mongo-Native 并尝试设置全局连接变量,但我对两种可能的解决方案感到困惑。各位大神能帮我看看哪个好? 1. 解决方案(这很糟糕,因为每个请求都会尝试创建一个新连接。)

var express = require('express');  
var app = express();  
var MongoClient = require('mongodb').MongoClient;  
var assert = require('assert');

// Connection URL
var url = '[connectionString]]';

// start server on port 3000
app.listen(3000, '0.0.0.0', function() {  
  // print a message when the server starts listening
  console.log("server starting");
});

// Use connect method to connect to the server when the page is requested
app.get('/', function(request, response) {  
  MongoClient.connect(url, function(err, db) {
    assert.equal(null, err);
    db.listCollections({}).toArray(function(err, collections) {
        assert.equal(null, err);
        collections.forEach(function(collection) {
            console.log(collection);
        });
        db.close();
    })
    response.send('Connected - see console for a list of available collections');
  });
});
  1. 解决方案(在应用程序初始化时连接并将连接字符串分配给全局变量)。但我认为将连接字符串分配给全局变量不是一个好主意。

    var mongodb; var url = '[连接字符串]'; MongoClient.connect(url, function(err, db) {
    assert.equal(null, err); mongodb=db; } );

我想在应用初始化时创建一个连接并在整个应用生命周期内使用。

你们能帮帮我吗?谢谢。

【问题讨论】:

  • 您可以制作一个仅包含数据库连接的文件,然后导入该连接变量并在需要时使用该导入变量
  • @UditKumawat 是的,我这样做了,但是 Node 的这个 mongo 库有一个用于连接的回调函数,我需要使用它,所以我需要再次等待它连接,然后启动我认为的应用程序。
  • 您可以声明一个全局变量,然后在初始化连接变量后使用它
  • 是的,我想过。但是,我遇到了这个Using Global Variables in Node.js

标签: node.js mongodb express node-mongodb-native


【解决方案1】:

创建一个Connection 单例模块来管理应用程序数据库连接。

MongoClient 不提供单例连接池,因此您不想在应用程序中重复调用MongoClient.connect()。用于包装 mongo 客户端的单例类适用于我见过的大多数应用程序。

const MongoClient = require('mongodb').MongoClient

class Connection {

    static async open() {
        if (this.db) return this.db
        this.db = await MongoClient.connect(this.url, this.options)
        return this.db
    }

}

Connection.db = null
Connection.url = 'mongodb://127.0.0.1:27017/test_db'
Connection.options = {
    bufferMaxEntries:   0,
    reconnectTries:     5000,
    useNewUrlParser:    true,
    useUnifiedTopology: true,
}

module.exports = { Connection }

require('./Connection') 在任何地方都可以使用Connection.open() 方法,如果已初始化,Connection.db 属性也将可用。

const router = require('express').Router()
const { Connection } = require('../lib/Connection.js')

// This should go in the app/server setup, and waited for.
Connection.open()

router.get('/files', async (req, res) => {
   try {
     const files = await Connection.db.collection('files').find({})
     res.json({ files })
   }
   catch (error) {
     res.status(500).json({ error })
   }
})

module.exports = router

【讨论】:

  • 是的,这是连接方法之一,但我需要在应用程序初始化时初始化此连接,并在应用程序生命周期内使其保持活动状态。使用上述方法,您可以连接,但您需要调用 Connection.connectToMongo() 函数来连接每个请求。
  • 您只需要在应用初始化时调用connect 一次,在server.listenapp.listen 附近。然后该类充当单例,在您require 连接类的每个地方,相同的(已连接)database 属性可供使用。
  • 谢谢@Matt。欣赏它。
  • 我相信if ( this.database ) return Promise.resolve(this.database)这一行将永远解析为false
  • Connection.db.collection('files').find({}) ^ TypeError: Cannot read property 'collection' of null
【解决方案2】:

另一种更直接的方法是利用 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 可以轻松访问它,并且不需要创建任何其他模块。

【讨论】:

  • 你是否可以在app.js 之外实例化mongo 连接,例如在config/database.js 中(只是为了保持文件“更干净”)?我已经尝试过了,但我一直在思考如何在config/database.js 中访问app
【解决方案3】:

我就是这样做的。

// custom class
const MongoClient = require('mongodb').MongoClient
const credentials = "mongodb://user:pass@mongo"

class MDBConnect {
    static connect (db, collection) {
        return MongoClient.connect(credentials)
            .then( client => {
                return client.db(db).collection(collection);
            })
            .catch( err => { console.log(err)});
    }
    static findOne(db, collection, query) {
        return MDBConnect.connect(db,collection)
            .then(c => {
                return c.findOne(query)
                            .then(result => {
                                return result;
                            });
            })
    }
    // create as many as you want
    //static find(db, collection, query)
    //static insert(db, collection, query)
    // etc etc etc
}
module.exports = MDBConnect;


// in the route file
var express = require('express');
var router = express.Router();
var ObjectId = require('mongodb').ObjectId; 
var MDBConnect =  require('../storage/MDBConnect');

// Usages
router.get('/q/:id', function(req, res, next) {
    let sceneId = req.params.id;
    
    // user case 1
    MDBConnect.connect('gameapp','scene')
        .then(c => {
            c.findOne({_id: ObjectId(sceneId)})
                .then(result => {
                    console.log("result: ",result);
                    res.json(result);
                })
        });
    // user case 2, with query
    MDBConnect.findOne('gameapp','scene',{_id: ObjectId(sceneId)})
        .then(result => {
            res.json(result);
        });
});

【讨论】:

    【解决方案4】:

    模块版本 ^3.1.8

    将连接初始化为承诺:

    const MongoClient = require('mongodb').MongoClient
    const uri = 'mongodb://...'
    const client = new MongoClient(uri)
    const connection = client.connect()
    

    然后在您希望对数据库执行操作时调用连接:

    app.post('/insert', (req, res) => {
        const connect = connection
        connect.then(() => {
            const doc = { id: 3 }
            const db = client.db('database_name')
            const coll = db.collection('collection_name')
            coll.insertOne(doc, (err, result) => {
                if(err) throw err
            })
        })
    })  
    

    【讨论】:

    • 这不会为每个请求创建一个新连接吗?想法是跨请求/路由重用相同的连接@henry-bothin
    【解决方案5】:

    在 Express 中,您可以像这样添加 mongo 连接

    import {MongoClient} from 'mongodb';
    import express from 'express';
    import bodyParser from 'body-parser';
        let mongoClient = null;
        MongoClient.connect(config.mongoURL, {useNewUrlParser: true, useUnifiedTopology: true},function (err, client) {
            if(err) {
              console.log('Mongo connection error');
            } else {
              console.log('Connected to mongo DB');
              mongoClient = client;
            }
        })
    let app = express();
    app.use(bodyParser.urlencoded({ extended: false }));
    app.use(bodyParser.json());
    
    app.use((req,res,next)=>{
        req.db = mongoClient.db('customer_support');
        next();
    });
    

    以后你可以作为 req.db 访问它

    router.post('/hello',async (req,res,next)=>{
        let uname = req.body.username;
        let userDetails = await getUserDetails(req.db,uname)
        res.statusCode = 200;
        res.data = userDetails;
        next();
    });
    

    【讨论】:

      【解决方案6】:

      我对答案进行了大量研究,但找不到可以说服我的解决方案,因此我开发了自己的解决方案。

      const {MongoClient} = require("mongodb");
      
      class DB {
          static database;
          static client;
      
          static async setUp(url) {
              if(!this.client) {
                  await this.setClient(url);
                  await this.setConnection();
              }
      
              return this.database;
          }
      
          static async setConnection() {
              this.database = this.client.db("default");
          }
      
          static async setClient(url) {
              console.log("Connecting to database");
              const client = new MongoClient(url);
      
              await client.connect();
      
              this.client = client;
          }
      }
      
      module.exports = DB;
      
      

      用法:

      const DB = require("./Path/to/DB");
      (async () => {
        const database = await DB.setUp();
        const users = await database.collection("users").findOne({ email: "" });
      });
      

      【讨论】:

        【解决方案7】:

        这是Matt's answer 的一个版本,可让您在使用连接时定义databasecollection。不确定它是否像他的解决方案一样“防水”,但评论太长了。

        我删除了Connection.options,因为他们给了我错误 (perhaps some options are deprecated?)。

        lib/Connection.js

        const MongoClient = require('mongodb').MongoClient;
        const { connection_string } = require('./environment_variables');
        
        class Connection {
          static async open() {
            if (this.conn) return this.conn;
            this.conn = await MongoClient.connect(connection_string);
            return this.conn;
          }
        }
        
        Connection.conn = null;
        Connection.url = connection_string;
        
        module.exports = { Connection };
        

        testRoute.js

        const express = require('express');
        const router = express.Router();
        const { Connection } = require('../lib/Connection.js');
        
        Connection.open();
        
        router.route('/').get(async (req, res) => {
            try {
                const query = { username: 'my name' };
                const collection = Connection.conn.db('users').collection('users');
                const result = await collection.findOne(query);
                res.json({ result: result });
            } catch (error) {
                console.log(error);
                res.status(500).json({ error });
            }
        });
        
        module.exports = router;
        

        如果您想从路由文件中取出中间件:

        testRoute.js 变为:

        const express = require('express');
        const router = express.Router();
        const test_middleware_01 = require('../middleware/test_middleware_01');
        
        router.route('/').get(test_middleware_01);
        
        module.exports = router;
        

        并且中间件在middleware/test_middleware_01.js中定义:

        const { Connection } = require('../lib/Connection.js');
        
        Connection.open();
        
        const test_middleware_01 = async (req, res) => {
            try {
                const query = { username: 'my name' };
                const collection = Connection.conn.db('users').collection('users');
                const result = await collection.findOne(query);
                res.json({ result: result });
            } catch (error) {
                console.log(error);
                res.status(500).json({ error });
            }
        };
        
        module.exports = test_middleware_01;
        

        【讨论】:

          猜你喜欢
          • 2010-09-10
          • 2014-01-25
          • 1970-01-01
          • 1970-01-01
          • 2020-04-17
          • 2019-10-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多