坛子里有人发了篇]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 回复
@jiyinyiyong 如果你的操作是顺序的,那没问题, 但正常情况,特别是web应用,操作基本上是并发的, 如果你的连接数跟不上并发数,那么会造成操作阻塞, 新来的操作必须等同一连接上的操作结束后才能继续