2013-03-28 22 views
21

Ho provato node.js con mongodb (2.2.2) utilizzando l'unità node.js nativa di 10gen.Qual è il modo giusto per gestire le connessioni Mongodb?

All'inizio è andato tutto bene. Ma quando si arriva alla parte relativa al benchmarking della concorrenza, si sono verificati molti errori. Frequenti collegano/chiusura con 1000 convergenze possono causare mongodb respingere ogni ulteriore richiesta di errore del tipo:

Error: failed to connect to [localhost:27017] 

Error: Could not locate any valid servers in initial seed list 

Error: no primary server found in set 

Inoltre, se un sacco di clienti del sistema senza esplicito vicino, ci vorranno minuti mongodb per rilevare e chiuderli. Che causerà anche problemi di connessione simili. (Usando /var/log/mongodb/mongodb.log per verificare lo stato della connessione)

Ho provato molto. Secondo il manuale, mongodb non ha limiti di connessione, ma l'opzione poolSize sembra non avere effetti per me.

Come ho lavorato solo con il modulo nativo nodo-mongodb, non sono molto sicuro di cosa abbia causato il problema. Che dire delle prestazioni in altre lingue e driver?

PS: Attualmente, l'utilizzo del pool autogestito è l'unica soluzione che ho trovato, ma l'utilizzo non può non risolvere il problema con il set di repliche. Secondo il mio test, il set di repliche sembra richiedere molto meno connessioni quindi mongodb standalone. Ma non ho idea del perché questo accada.

codice di prova

Concurrency:

var MongoClient = require('mongodb').MongoClient; 

var uri = "mongodb://192.168.0.123:27017,192.168.0.124:27017/test"; 

for (var i = 0; i < 1000; i++) { 
    MongoClient.connect(uri, { 
     server: { 
      socketOptions: { 
       connectTimeoutMS: 3000 
      } 
     }, 
    }, function (err, db) { 
     if (err) { 
      console.log('error: ', err); 
     } else { 
      var col = db.collection('test'); 
      col.insert({abc:1}, function (err, result) { 
       if (err) { 
        console.log('insert error: ', err); 
       } else { 
        console.log('success: ', result); 
       } 
       db.close() 
      }) 
     } 
    }) 
} 

soluzione generica-pool:

var MongoClient = require('mongodb').MongoClient; 
var poolModule = require('generic-pool'); 

var uri = "mongodb://localhost/test"; 

var read_pool = poolModule.Pool({ 
    name  : 'redis_offer_payment_reader', 
    create : function(callback) { 
     MongoClient.connect(uri, {}, function (err, db) { 
      if (err) { 
       callback(err); 
      } else { 
       callback(null, db); 
      } 
     }); 
    }, 
    destroy : function(client) { client.close(); }, 
    max  : 400, 
    // optional. if you set this, make sure to drain() (see step 3) 
    min  : 200, 
    // specifies how long a resource can stay idle in pool before being removed 
    idleTimeoutMillis : 30000, 
    // if true, logs via console.log - can also be a function 
    log : false 
}); 


var size = []; 
for (var i = 0; i < 100000; i++) { 
    size.push(i); 
} 

size.forEach(function() { 
    read_pool.acquire(function (err, db) { 
     if (err) { 
      console.log('error: ', err); 
     } else { 
      var col = db.collection('test'); 
      col.insert({abc:1}, function (err, result) { 
       if (err) { 
        console.log('insert error: ', err); 
       } else { 
        //console.log('success: ', result); 
       } 
       read_pool.release(db); 
      }) 
     } 
    }) 
}) 

risposta

22

Da Node.js è thread singolo non dovrebbe essere aprendo e chiudendo la connessione su ogni richiesta (come te farebbe in altri ambienti multi-thread.)

Questa è una citazione dalla persona che ha scritto il modulo client node.js MongoDB:

"Si apre MongoClient.connect una volta quando l'app si avvia e riutilizza l'oggetto db . Non è un pool di connessione singleton ogni .connect crea un nuovo pool di connessioni. Quindi aprire una volta un [d] riutilizzare in tutti i richieste “-. Christkv https://groups.google.com/forum/#!msg/node-mongodb-native/mSGnnuG8C1o/Hiaqvdu1bWoJ

+0

Anche se Node.js utilizza un unico processo e modello di eventi, ma le azioni IO deve essere sempre asincrona. Quale penso possa non essere la causa dei miei problemi. Ma, proverò nel modo in cui hai citato, grazie. – Jack

+2

Ti manca il punto, nel tuo primo esempio; Quel codice equivale a far partire la tua app Mongo 1000 volte. Dovresti solo chiamare .connect una volta nella tua app. –

+0

Sì, questo è un problema, e il motivo è che MongoClient mantiene un pool di connessioni stesso che diventa più costoso di altri dbs. Ma non il problema con il modello del singolo processo node.js. – Jack

3

Dopo aver guardato in consiglio di Hector. Trovo che la connessione di Mongodb sia molto diversa da altri database che ho mai usato. La differenza principale è un'unità nativo in nodejs: MongoClient ha il proprio pool di connessione per ogni MongoClient aperto, che dimensione del pool definito da

server:{poolSize: n} 

Quindi, aperta collegamento 5 MongoClient con poolSize: 100, significa totale 5 * 100 = 500 connessioni al bersaglio Mongodb Uri. In questo caso, frequente apertura & chiudi connessioni MongoClient costituirà sicuramente un enorme onere per l'host e, infine, causare problemi di connessione. Ecco perché ho avuto così tanti problemi in primo luogo.

Tuttavia, poiché il mio codice è stato scritto in questo modo, utilizzo un pool di connessioni per memorizzare una singola connessione con ciascun URI distinto e utilizzare un limitatore parallelo semplice della stessa dimensione di poolSize per evitare errori di connessione del picco di carico .

Ecco il mio codice:

/*npm modules start*/ 
var MongoClient = require('mongodb').MongoClient; 
/*npm modules end*/ 

// simple resouce limitation module, control parallel size 
var simple_limit = require('simple_limit').simple_limit; 

// one uri, one connection 
var client_pool = {}; 

var default_options = { 
    server: { 
     auto_reconnect:true, poolSize: 200, 
     socketOptions: { 
      connectTimeoutMS: 1000 
     } 
    } 
} 

var mongodb_pool = function (uri, options) { 
    this.uri = uri; 
    options = options || default_options; 
    this.options = options; 
    this.poolSize = 10; // default poolSize 10, this will be used in generic pool as max 

    if (undefined !== options.server && undefined !== options.server.poolSize) { 
     this.poolSize = options.server.poolSize;// if (in)options defined poolSize, use it 
    } 
} 

// cb(err, db) 
mongodb_pool.prototype.open = function (cb) { 
    var self = this; 
    if (undefined === client_pool[this.uri]) { 
     console.log('new'); 

     // init pool node with lock and wait list with current callback 
     client_pool[this.uri] = { 
      lock: true, 
      wait: [cb] 
     } 

     // open mongodb first 
     MongoClient.connect(this.uri, this.options, function (err, db) { 
      if (err) { 
       cb(err); 
      } else { 
       client_pool[self.uri].limiter = new simple_limit(self.poolSize); 
       client_pool[self.uri].db = db; 

       client_pool[self.uri].wait.forEach(function (callback) { 
        client_pool[self.uri].limiter.acquire(function() { 
         callback(null, client_pool[self.uri].db) 
        }); 
       }) 

       client_pool[self.uri].lock = false; 
      } 
     }) 
    } else if (true === client_pool[this.uri].lock) { 
     // while one is connecting to the target uri, just wait 
     client_pool[this.uri].wait.push(cb); 
    } else { 
     client_pool[this.uri].limiter.acquire(function() { 
      cb(null, client_pool[self.uri].db) 
     }); 
    } 
} 

// use close to release one connection 
mongodb_pool.prototype.close = function() { 
    client_pool[this.uri].limiter.release(); 
} 

exports.mongodb_pool = mongodb_pool; 
+0

Ciao, ho riscontrato questo problema. e potresti superare 'var simple_limit = require ('simple_limit'). simple_limit; 'codice simple_limit.js? e come usare 'mongodb_pool'? grazie ~ – Eddy

Problemi correlati