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’问题的三种方案总结

posted @ 2014-12-03 09:51  zlz~ling  阅读(1162)  评论(0编辑  收藏  举报