Nodejs 自己随便写的一个对象池

Nodejs 自己随便写的一个对象池

坛子里有人发了篇]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
});

6 回复

Nodejs 自己随便写的一个对象池

坛子里有人发了一篇关于 MongoDB 驱动的正确使用方法的文章,其中通过对象池来解决连接管理问题。文中提到使用了现有的 generic_pool 模块,但我决定自己写一个简单的对象池,主要是因为我觉得现有的模块可能没有完全解决开发过程中可能会忽略的小错误。比如,开发人员忘记在操作结束后调用 pool.release,这会导致对象池不断创建新的链接,最终可能导致资源耗尽。

对 Array 的扩展

为了方便管理和操作对象池中的对象,我们先对 JavaScript 的数组进行一些扩展:

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 < option.minConnect; 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 = [];

        objectPool.busy.forEach(function(obj) {
            option.objectRelease(obj);
        });
        objectPool.busy = [];
        totalObjCount = 0;
    };
}
module.exports = exports = objectPool;

使用方法

接下来是使用这个对象池的具体方法,这里以 MongoDB 为例:

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

通过这种方式,我们可以有效地管理和复用数据库连接,避免了频繁创建和关闭连接导致的性能损耗。


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

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

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

#顶一下

对象池是一种常见的技术,用于管理和复用对象实例,从而减少频繁创建和销毁对象所带来的性能开销。下面提供一个简单的Node.js对象池实现,并通过一个示例展示其使用方法。

示例代码

首先,我们需要扩展Array原型以增加一些辅助方法:

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

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

接下来,我们定义一个objectPool类,用于管理对象池:

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

    this.getOne = function() {
        if (objectPool.free.length > 0) {
            let obj = objectPool.free.pop();
            obj.startBusyTime = new Date();
            objectPool.busy.push(obj);
            return obj;
        } else {
            let newObj = option.objectCreate();
            totalObjCount++;
            newObj.startBusyTime = new Date();
            objectPool.busy.push(newObj);
            return newObj;
        }
    };

    this.freeOne = function(obj) {
        let elementIndex = objectPool.busy.indexOf(obj);
        if (elementIndex > -1) {
            objectPool.busy.remove(obj);
            objectPool.free.push(obj);
        }
    };

    this.init = function() {
        if (totalObjCount < option.minConnect) {
            for (let i = totalObjCount; i < option.minConnect; i++) {
                let newObj = option.objectCreate();
                totalObjCount++;
                objectPool.free.push(newObj);
            }
        }
    };

    this.manager = function() {
        if (!this.managerTimmer) {
            this.managerTimmer = setInterval(() => {
                let needToFreeObjs = objectPool.busy.where((o) => {
                    let now = new Date();
                    if ((now - o.startBusyTime) >= option.maxBusyTime) return true;
                    else return false;
                });
                needToFreeObjs.forEach((o) => {
                    this.freeOne(o);
                }.bind(this));

                if (totalObjCount > option.maxConnect && objectPool.free.length > 0) {
                    for (let i = totalObjCount; i > option.maxConnect && objectPool.free.length > 0; i--) {
                        let obj = objectPool.free.pop();
                        totalObjCount--;
                        option.objectRelease(obj);
                    }
                }
            }.bind(this), option.managerSpan * 1000);
        }
    };

    this.destroy = function() {
        objectPool.free.forEach((obj) => {
            option.objectRelease(obj);
        });
        objectPool.free = [];

        objectPool.busy.forEach((obj) => {
            option.objectRelease(obj);
        });
        objectPool.busy = [];
        totalObjCount = 0;
    };
}

使用方法

假设我们要创建一个MongoDB连接池:

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

通过上述代码,我们创建了一个简单的对象池,并将其应用于MongoDB连接管理。这样可以确保在需要时能够快速获取已有的连接实例,避免了频繁创建和销毁连接带来的性能损耗。

回到顶部