MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Connexion dynamique à la base de données mongodb ou mongoose depuis nodejs

C'est pour aider d'autres personnes qui pourraient se trouver dans une situation similaire à la mienne. J'espère qu'il pourra être standardisé. Je ne pense pas que nous devrions avoir à réinventer la roue à chaque fois que quelqu'un a besoin de faire une application multi-locataire.

Cet exemple décrit une structure multi-tenant avec chaque client ayant sa propre base de données.

Voici donc les objectifs visés par cette solution :

  • chaque client est identifié par un sous-domaine, par exemple client1.application.com,
  • l'application vérifie si le sous-domaine est valide,
  • l'application recherche et obtient les informations de connexion (URL de la base de données, informations d'identification, etc.) à partir de la base de données principale,
  • l'application se connecte à la base de données client (passe pratiquement le relais au client),
  • l'application prend des mesures pour garantir l'intégrité et la gestion des ressources (par exemple, utilisez la même connexion à la base de données pour les membres du même client, plutôt que d'établir une nouvelle connexion).

Voici le code

dans votre app.js fichier

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

J'ai créé deux middlewares :

  • clientListener - d'identifier le client qui se connecte,
  • setclientdb - obtient les détails du client à partir de la base de données principale, une fois le client identifié, puis établit la connexion à la base de données client.

Intergiciel clientListener

Je vérifie qui est le client en vérifiant le sous-domaine à partir de l'objet de requête. Je fais un tas de vérifications pour m'assurer que le client est valide (je sais que le code est compliqué et peut être rendu plus propre). Après m'être assuré que le client est valide, je stocke les informations du client en session. Je vérifie également que si les informations des clients sont déjà stockées dans la session, il n'est pas nécessaire d'interroger à nouveau la base de données. Nous devons juste nous assurer que le sous-domaine de la requête correspond à celui qui est déjà stocké dans la session.

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;

Intergiciel setclientdb :

Je revérifie tout en m'assurant que le client est valide. Ensuite, la connexion à la base de données du client avec les informations récupérées de la session est ouverte.

Je m'assure également de stocker toutes les connexions actives dans un objet global, afin d'empêcher de nouvelles connexions à la base de données à chaque requête (nous ne voulons pas surcharger chaque serveur mongodb client avec des connexions).

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;

Le dernier mais non le moindre

Puisque j'utilise une combinaison de mangouste et de mongo natif, nous devons compiler nos modèles au moment de l'exécution. Veuillez voir ci-dessous

Ajoutez ceci à votre 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();
});

Explication :

Comme je l'ai dit plus tôt, j'ai créé un objet global pour stocker l'objet de connexion à la base de données active :global.App.activdb

Ensuite, j'utilise cet objet de connexion pour créer (compiler) un modèle de mangouste, après l'avoir stocké dans la propriété db de l'objet req :req.db . Je fais cela pour pouvoir accéder à mes modèles dans mon contrôleur comme celui-ci par exemple.

Exemple de mon contrôleur d'utilisateurs :

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

};

Je reviendrai et je finirai par nettoyer ça. Si quelqu'un veut m'aider, c'est gentil.