mongodb数据库调试问题:‘db object already connecting, open cannot be called multiple times’
在微博小系统的调试过程中:
(1)登入登出可以正常显示,就是在注册的时候网络连接突然停止,但是用户名和密码已经存入数据库中,报错为:undefined is not a function
错误主要指向Use.js中的一句话:mongodb.close();
解决方案:因为mongodb版本高于1.3.0,所以认为mongodb.close()这个函数未定义而当做函数来用,直接把mongodb.close()这句话注释掉则系统可以正常注册运行;
(2)注释掉mongodb.close()以后系统又出现mongodb经常会出现的错误db object already connecting, open cannot be called multiple times’
nodejs是异步线程,无论如何,都要用到db.open(),但是每次打开数据库访问完毕后还得对应关闭书裤裤db.close();
在上一步调试中注释掉关闭数据库,则数据库一直是处于打开状态,数据库还未关闭就在此打开则会出现db object already connecting, open cannot be called multiple times’的错误
此时的解决方案:
a:在数据库在此打开之前直接在前面加上一个mongodb.close()以确保每次数据库打开之前已经关闭数据库,经调试系统正常运行;
b.从open and close 行为改为 open once and reuse anywhere。程序启动的时候db.open一次,每次http请求直接访问数据库,扔掉db.open/db.close这2个多余的操作
原始的open + close方法:
var server_options={}; var db_options={w:-1}; var mongodb = require("mongodb"), mongoserver = new mongodb.Server('localhost', 27017,server_options ), db = new mongodb.Db('test', mongoserver, db_options); var http=require('http'); var server=http.createServer(function(req,res){ db.open(function(err,db){ if(err)return console.error(err); console.log('* mongodb connected'); db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); db.close(); }); }) }); server.listen(8080,function(){ console.log('server listen to %d',this.address().port); }); setTimeout(function(){ //http.get('http://localhost:8080',function(res){console.log('request ok')}); //http.get('http://localhost:8080',function(res){console.log('request ok')}); },2000);
扔掉db.open/db.close这2个循环对应操作的方法,让其最开始就一直处于打开状态
var server_options={'auto_reconnect':true,poolSize:5}; var db_options={w:-1}; var mongodb = require("mongodb"), mongoserver = new mongodb.Server('localhost', 27017,server_options ), db = new mongodb.Db('test', mongoserver, db_options); db.open(function(err,db){ if(err)throw err; console.info('mongodb connected'); }); var http=require('http'); var server=http.createServer(function(req,res){ db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); }); }); server.listen(8080,function(){ console.log('server listen to %d',this.address().port); }); setTimeout(function(){ http.get('http://localhost:8080',function(res){console.log('request ok')}); http.get('http://localhost:8080',function(res){console.log('request ok')}); },2000);
这样改会引入潜在问题:当并发访问>5时,因为同时可用的底层数据库连接只有5,从而导致了阻塞。
实际应用场景中,直接引用db对象并不是一个好主意。默认情况下,db的poolSize=5,意味着并发只有5, 要提高并发的话,把poolSize拉到10? 20? 50? 100? NO,我们需要的是能动态调整连接数的连接池,既能满足高峰期的连接数要求,也能在空闲期释放闲置的连接,而不是象mongodb的内置连接池那样保持固定连接数。怎么办?重新发明轮子吗?不,重用已有的连接池模块generic_pool,代码如下
var http=require('http'), mongodb = require("mongodb"), poolModule = require('generic-pool'); var pool = poolModule.Pool({ name : 'mongodb', create : function(callback) { var server_options={'auto_reconnect':false,poolSize:1}; var db_options={w:-1}; var mongoserver = new mongodb.Server('localhost', 27017,server_options ); var db=new mongodb.Db('test', mongoserver, db_options); db.open(function(err,db){ if(err)return callback(err); callback(null,db); }); }, destroy : function(db) { db.close(); }, max : 10,//根据应用的可能最高并发数设置 idleTimeoutMillis : 30000, log : false }); var server=http.createServer(function(req,res){ pool.acquire(function(err, db) { if (err) { res.statusCode=500; res.end(JSON.stringify(err,null,2)); } else { db.collection('foo').save({test:1},function(err,result){ res.end(JSON.stringify(result,null,2)); pool.release(db); }); } }); }); server.listen(8080,function(){ console.log('server listen to %d',this.address().port); }); setTimeout(function(){ http.get('http://localhost:8080',function(res){console.log('request ok')}); http.get('http://localhost:8080',function(res){console.log('request ok')}); },2000);
c.对于mongodb数据库有一个问题:刷新得太快,或者多个用户同时访问数据库,数据库没来得及关闭,db object already connecting, open cannot be called multiple times这个错误就会出现
我们如果换成使用mongoose就不会出现这错误,因为对于mongoose而言,一旦连接好数据库,db就会处于open状态,不存在访问时要打开,然后又要关闭的规则,
这是用mongodb的操作方法:
User.get = function get(username, callback) { mongodb.open(function(err, db) { if (err) { return callback(err); } //读取 users 集合 db.collection('users', function(err, collection) { if (err) { mongodb.close(); return callback(err); } //查找 name 属性为 username 的文档 collection.findOne({name: username}, function(err, doc) { mongodb.close(); if (doc) callback (err, doc); else callback (err, null); }); }); }); };
这是用mongoose的操作方法
User.get = function get(username, callback) { users.findOne({name:username}, function(err, doc){ if (err) { return callback(err, null); } return callback(err, doc); }); };
以上a.b.c则为解决db object already connecting, open cannot be called multiple times’问题的三种方案总结