自己随便写的一个对象池
发布于 2年前 作者 saber 1564 次浏览

坛子里有人发了篇]mongodb驱动的正确使用方法,里面通过对象池来解决的思路很正确,文中提到用了现有的generic_pool模块,这里我发一个自己写的简单对象池,只所以自己写是我觉得generic_pool可能没解决一个开发过程中可以忽略不计的失误, 就是当开发人员忘了在操作结束调用pool.release,这个问题在现实中可能大家都不会犯,但谁又能肯定在半夜昏昏沉沉的时候就偏偏漏掉呢,一旦忘了释放, 对象池就会源源不断打开新的链接, 所以我就写了一个简单的对象池模块, 运行下来效果还行

先对Array做一些扩展

Array.prototype.where = function(whereFunc){
    var tmp = [];
    this.forEach(function(element){
        if (whereFunc(element)){
            tmp.push(element);
        }
    });

    return tmp;
};

Array.prototype.remove = function(element){
    var elementIndex = this.indexOf(element);
    if(elementIndex > -1){
        this.splice(elementIndex, 1);
    }
};

ObjectPool.js

function objectPool(option){
    var totalObjCount = 0;
    var objectPool={
        free:[],
        busy:[]
    };

    /**
     * 从对象池中获取一个对象,当没有可用的对象时候创建新的对象
     * */
    this.getOne = function(){
        if(objectPool.free.length > 0) {
            var obj = objectPool.free.pop();
            obj.startBusyTime = new Date();
            objectPool.busy.push(obj);
            return obj;
        }
        else {
            var newObj = option.objectCreate();
            totalObjCount++;
            newObj.startBusyTime = new Date();
            objectPool.busy.push(newObj);
            return newObj;
        }

    };

    //释放对象池中的对象
    this.freeOne = function(obj){
        var elementIndex = objectPool.busy.indexOf(obj);
        if(elementIndex > -1){
            objectPool.busy.remove(obj);
            objectPool.free.push(obj);
        }
    };


    this.init = function(){
        //对象池中的对象小于最小数
        if(totalObjCount < option.minConnect) {
            for(var i=totalObjCount; i<totalObjCount; i++){
                var newObj = option.objectCreate();
                totalObjCount++;
                objectPool.free.push(newObj);
            }
        }
    };

    //对象池维护函数,防止产生过多的对象
    this.manager = function(){
        if(!this.managerTimmer){
            this.managerTimmer = setInterval(function(){

                //将超过最大忙碌时间的对象挪回空闲队列
                var needToFreeObjs = objectPool.busy.where(function(o){
                    var now = new Date();
                    if((now - o.startBusyTime) >= option.maxBusyTime)
                        return true;
                    else
                        return false;
                });
                needToFreeObjs.forEach(function(o){
                    this.freeOne(o);
                }.bind(this));

                //产生的对象数已经超过最大限制数,且有空闲对象,可以将空闲对象释放
                if(totalObjCount > option.maxConnect && objectPool.free.length > 0) {
                    for(var i=totalObjCount; i>option.maxConnect && objectPool.free.length > 0; i--){
                        var obj = objectPool.free.pop();
                        totalObjCount--;
                        option.objectRelease(obj);
                    }
                }

            }.bind(this), option.managerSpan*1000);
        }
    };

    //销毁对象池,释放所有对象
    this.destroy = function(){
        objectPool.free.forEach(function(obj){
            option.objectRelease(obj);
        });
        objectPool.free = Array.Empty();

        objectPool.busy.forEach(function(obj){
            option.objectRelease(obj);
        });
        objectPool.busy = Array.Empty();
        totalObjCount = 0;
    };


}

module.exports = exports = objectPool;

使用方法,我用的是Mongoskin

var mongoSkin = require('mongoskin');
var objectPool = require('./ObjectPool.js');
var mongo = {
    hostname:'127.0.0.1',
    port:27017,
    db:'test'
};

var generate_mongo_url = function(obj){
    obj.hostname = obj.hostname || 'localhost';
    obj.port = obj.port || 27017;
    obj.db = obj.db || 'test';
    if(obj.username && obj.password){
        return obj.username + ':' + obj.password + '@' + obj.hostname + ':' + obj.port + '/' + obj.db;
    }
    else {
        return obj.hostname + ':' + obj.port + '/' + obj.db;
    }
};

var poolOption={
    minConnect:5,
    maxConnect:50,
    managerSpan:120,  //对象池维护周期,单位秒
    maxBusyTime:60000, //最大工作时间,超过这个时间说明开发人员可能漏了释放资源
    objectCreate:function(){
        return mongoSkin.db(generate_mongo_url(mongo));
    },
    objectRelease:function(obj){
        obj.close();
    }
}


var dbPool = new objectPool(poolOption);  //创建对象池
dbPool.manager();  //开始对象池维护
process.on('exit', function(){
    dbPool.destroy(); //程序退出,销毁对象池
});


function DBConnection(){
    this.open = function(){
        if(!this.db) {
            this.db = dbPool.getOne();
        }
        return this;
    };

    this.close = function(){
        if(this.db) {
            dbPool.freeOne(this.db);
            this.db = null;
        }
    };
};

exports.DBConnection = DBConnection;


var dbConnection = new DBConnection().open();

dbConnection.db.collection('category').find({}, function(err, cs){
     dbConnection.close(); //这里你可以不写,超过最大工作时间后资源会自动归入空闲队列
    //TODO
});
7 回复

光写前端对对象池概念真是好陌生, 这是为了数据库性能做的模块么?

嗯,是的,主要是减少重复连接数据库的开销,不知道Mongo的连接开销是多少,但是建立连接肯定是有开销的,

@saber Node 使用数据库的时候 Mongo 是经常断开是么? 以前写玩具时候我都直接连接一次用到底的, 这么说方法不对

@jiyinyiyong 如果你的操作是顺序的,那没问题, 但正常情况,特别是web应用,操作基本上是并发的, 如果你的连接数跟不上并发数,那么会造成操作阻塞, 新来的操作必须等同一连接上的操作结束后才能继续

@saber 原来还要考虑并发的情况. 学习了

这种方式真的不推荐使用,连接池还是由驱动保持吧

回到顶部