MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Dynamische Datenbankverbindung zu mongodb oder mongoose von nodejs

Dies soll anderen helfen, die sich möglicherweise in einer ähnlichen Situation wie ich befinden. Ich hoffe, dass es standardisiert werden könnte. Ich denke nicht, dass wir das Rad jedes Mal neu erfinden müssen, wenn jemand eine mandantenfähige Anwendung erstellen muss.

Dieses Beispiel beschreibt eine Multi-Tenant-Struktur, bei der jeder Client seine eigene Datenbank hat. Wie ich schon sagte, es könnte einen besseren Weg geben, aber da ich selbst keine Hilfe bekommen habe, war dies meine Lösung.

Hier sind also die Ziele, auf die diese Lösung abzielt:

  • jeder Client wird durch eine Subdomain identifiziert, z. B. client1.application.com,
  • Anwendung prüft, ob Subdomain gültig ist,
  • Anwendung sucht und erhält Verbindungsinformationen (Datenbank-URL, Anmeldeinformationen usw.) von der Master-Datenbank,
  • Anwendung verbindet sich mit Client-Datenbank (übergibt praktisch an Client),
  • Die Anwendung ergreift Maßnahmen, um die Integrität und das Ressourcenmanagement sicherzustellen (z. B. dieselbe Datenbankverbindung für Mitglieder desselben Clients verwenden, anstatt eine neue Verbindung herzustellen).

Hier ist der Code

in Ihrer app.js Datei

app.use(clientListener()); // checks and identify valid clients
app.use(setclientdb());// sets db for valid clients

Ich habe zwei Middlewares erstellt:

  • clientListener - um den sich verbindenden Client zu identifizieren,
  • setclientdb - Ruft Kundendetails aus der Master-Datenbank ab, nachdem der Kunde identifiziert wurde, und stellt dann eine Verbindung zur Kundendatenbank her.

clientListener-Middleware

Ich überprüfe, wer der Client ist, indem ich die Subdomain aus dem Anforderungsobjekt überprüfe. Ich mache eine Reihe von Überprüfungen, um sicherzustellen, dass der Client gültig ist (ich weiß, dass der Code chaotisch ist und sauberer gemacht werden kann). Nachdem ich sichergestellt habe, dass der Client gültig ist, speichere ich die Client-Informationen in der Sitzung. Ich überprüfe auch, dass, wenn die Kundeninformationen bereits in der Sitzung gespeichert sind, die Datenbank nicht erneut abgefragt werden muss. Wir müssen nur sicherstellen, dass die angeforderte Subdomain mit der bereits in der Sitzung gespeicherten übereinstimmt.

var Clients = require('../models/clients');
var basedomain = dbConfig.baseDomain;
var allowedSubs = {'admin':true, 'www':true };
allowedSubs[basedomain] = true;
function clientlistener() {
return function(req, res, next) {
    //console.dir('look at my sub domain  ' + req.subdomains[0]);
    // console.log(req.session.Client.name);

    if( req.subdomains[0] in allowedSubs ||  typeof req.subdomains[0] === 'undefined' || req.session.Client && req.session.Client.name === req.subdomains[0] ){
        //console.dir('look at the sub domain  ' + req.subdomains[0]);
        //console.dir('testing Session ' + req.session.Client);
        console.log('did not search database for '+ req.subdomains[0]);
        //console.log(JSON.stringify(req.session.Client, null, 4));
        next();
    }
    else{

        Clients.findOne({subdomain: req.subdomains[0]}, function (err, client) {
            if(!err){
                if(!client){
                    //res.send(client);
                    res.send(403, 'Sorry! you cant see that.');
                }
                else{
                    console.log('searched database for '+ req.subdomains[0]);
                    //console.log(JSON.stringify(client, null, 4));
                    //console.log(client);
                   // req.session.tester = "moyo cow";
                    req.session.Client = client;
                    return next();

                }
            }
            else{
                console.log(err);
                return next(err)
            }

        });
    }

   }
 }

module.exports = clientlistener;

setclientdb-Middleware:

Ich überprüfe alles noch einmal, um sicherzustellen, dass der Client gültig ist. Dann wird die Verbindung zur Datenbank des Kunden mit den aus der Sitzung abgerufenen Informationen geöffnet.

Ich stelle auch sicher, dass alle aktiven Verbindungen in einem globalen Objekt gespeichert werden, um neue Verbindungen zur Datenbank bei jeder Anfrage zu verhindern (wir wollen nicht jeden Client-Mongodb-Server mit Verbindungen überlasten).

var mongoose = require('mongoose');
//var dynamicConnection = require('../models/dynamicMongoose');
function setclientdb() {
    return function(req, res, next){
        //check if client has an existing db connection                                                               /*** Check if client db is connected and pooled *****/
    if(/*typeof global.App.clientdbconn === 'undefined' && */ typeof(req.session.Client) !== 'undefined' && global.App.clients[req.session.Client.name] !== req.subdomains[0])
    {
        //check if client session, matches current client if it matches, establish new connection for client
        if(req.session.Client && req.session.Client.name === req.subdomains[0] )
        {
            console.log('setting db for client ' + req.subdomains[0]+ ' and '+ req.session.Client.dbUrl);
            client = mongoose.createConnection(req.session.Client.dbUrl /*, dbconfigoptions*/);


            client.on('connected', function () {
                console.log('Mongoose default connection open to  ' + req.session.Client.name);
            });
            // When the connection is disconnected
            client.on('disconnected', function () {
                console.log('Mongoose '+ req.session.Client.name +' connection disconnected');
            });

            // If the Node process ends, close the Mongoose connection
            process.on('SIGINT', function() {
                client.close(function () {
                    console.log(req.session.Client.name +' connection disconnected through app termination');
                    process.exit(0);
                });
            });

            //If pool has not been created, create it and Add new connection to the pool and set it as active connection

            if(typeof(global.App.clients) === 'undefined' || typeof(global.App.clients[req.session.Client.name]) === 'undefined' && typeof(global.App.clientdbconn[req.session.Client.name]) === 'undefined')
            {
                clientname = req.session.Client.name;
                global.App.clients[clientname] = req.session.Client.name;// Store name of client in the global clients array
                activedb = global.App.clientdbconn[clientname] = client; //Store connection in the global connection array
                console.log('I am now in the list of active clients  ' + global.App.clients[clientname]);
            }
            global.App.activdb = activedb;
            console.log('client connection established, and saved ' + req.session.Client.name);
            next();
        }
        //if current client, does not match session client, then do not establish connection
        else
        {
            delete req.session.Client;
            client = false;
            next();
        }
    }
    else
    {
        if(typeof(req.session.Client) === 'undefined')
        {
           next();
        }
        //if client already has a connection make it active
        else{
            global.App.activdb = global.App.clientdbconn[req.session.Client.name];
            console.log('did not make new connection for ' + req.session.Client.name);
            return next();
        }

    }
    }
}

module.exports = setclientdb;

Zu guter Letzt

Da ich eine Kombination aus Mongoose und nativem Mongo verwende, müssen wir unsere Modelle zur Laufzeit kompilieren. Siehe unten

Fügen Sie dies zu Ihrer app.js hinzu

// require your models directory
var models = require('./models');

// Create models using mongoose connection for use in controllers
app.use(function db(req, res, next) {
    req.db = {
        User: global.App.activdb.model('User', models.agency_user, 'users')
        //Post: global.App.activdb.model('Post', models.Post, 'posts')
    };
    return next();
});

Erklärung:

Wie ich bereits sagte, habe ich ein globales Objekt erstellt, um das aktive Datenbankverbindungsobjekt zu speichern:global.App.activdb

Dann verwende ich dieses Verbindungsobjekt, um ein Mungo-Modell zu erstellen (kompilieren), nachdem ich es in der db-Eigenschaft des req-Objekts gespeichert habe:req.db . Ich mache das, damit ich zum Beispiel so auf meine Modelle in meinem Controller zugreifen kann.

Beispiel für meinen Users-Controller:

exports.list = function (req, res) {
    req.db.User.find(function (err, users) {

        res.send("respond with a resource" + users + 'and connections  ' + JSON.stringify(global.App.clients, null, 4));
        console.log('Worker ' + cluster.worker.id + ' running!');
    });

};

Ich werde zurückkommen und das irgendwann aufräumen. Wenn mir jemand helfen will, wäre das nett.