sql >> Database >  >> NoSQL >> MongoDB

Dynamische databaseverbinding met mongodb of mangoest van nodejs

Dit is om anderen te helpen die zich in een vergelijkbare situatie bevinden als ik. Ik hoop dat het gestandaardiseerd kan worden. Ik denk niet dat we het wiel opnieuw moeten uitvinden telkens wanneer iemand een multi-tenant applicatie moet maken.

Dit voorbeeld beschrijft een structuur met meerdere huurders waarbij elke klant zijn eigen database heeft. Zoals ik al zei, is er misschien een betere manier om dit te doen, maar omdat ik zelf geen hulp kreeg, was dit mijn oplossing.

Dus hier zijn de doelen die deze oplossing beoogt:

  • elke klant wordt geïdentificeerd door een subdomein, bijvoorbeeld client1.application.com,
  • toepassing controleert of subdomein geldig is,
  • toepassing zoekt verbindingsinformatie op (database-URL, inloggegevens, enz.) van hoofddatabase, en verkrijgt deze,
  • applicatie maakt verbinding met klantendatabase (overhandigt vrijwel aan klant),
  • applicatie neemt maatregelen om integriteit en resourcebeheer te waarborgen (gebruik bijvoorbeeld dezelfde databaseverbinding voor leden van dezelfde client, in plaats van een nieuwe verbinding te maken).

Hier is de code

in uw app.js bestand

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

Ik heb twee middlewares gemaakt:

  • clientListener - om de verbinding van de klant te identificeren,
  • setclientdb - haalt klantgegevens op uit de hoofddatabase, nadat de klant is geïdentificeerd, en maakt vervolgens verbinding met de klantendatabase.

clientListener-middleware

Ik controleer wie de klant is door het subdomein van het aanvraagobject te controleren. Ik doe een aantal controles om er zeker van te zijn dat de client geldig is (ik weet dat de code rommelig is en schoner kan worden gemaakt). Nadat ik ervoor heb gezorgd dat de klant geldig is, sla ik de informatie van de klant op in sessie. Ik controleer ook dat als de klanteninformatie al in de sessie is opgeslagen, het niet nodig is om de database opnieuw te doorzoeken. We moeten er alleen voor zorgen dat het subdomein van het verzoek overeenkomt met het domein dat al in de sessie is opgeslagen.

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:

Ik controleer alles opnieuw om ervoor te zorgen dat de client geldig is. Vervolgens wordt de verbinding met de database van de klant geopend met de informatie die is opgehaald uit de sessie.

Ik zorg er ook voor dat alle actieve verbindingen in een globaal object worden opgeslagen, om te voorkomen dat er bij elk verzoek nieuwe verbindingen met de database worden gemaakt (we willen niet elke mongodb-server van elke klant overbelasten met verbindingen).

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;

Last but not least

Omdat ik een combinatie van mangoest en inheemse mongo gebruik, moeten we onze modellen tijdens runtime compileren. Zie hieronder

Voeg dit toe aan je app.js

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

Uitleg:

Zoals ik al eerder zei, heb ik een globaal object gemaakt om het actieve databaseverbindingsobject op te slaan:global.App.activdb

Vervolgens gebruik ik dit verbindingsobject om het mangoestmodel te maken (compileren), nadat ik het heb opgeslagen in de eigenschap db van het req-object:req.db . Ik doe dit zodat ik bijvoorbeeld op deze manier toegang heb tot mijn modellen in mijn controller.

Voorbeeld van mijn gebruikerscontroller:

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

};

Ik zal terugkomen en dit uiteindelijk opruimen. Als iemand me wil helpen, graag.



  1. Hoe een array van objecten in een array in mangoest te duwen met één oproep?

  2. MongoDB $toDate

  3. MongoDB dropIndexes()

  4. Mongoose-subdocumenten versus genest schema