关于generic-pool与mongodb在Nodejs中的性能问题
关于generic-pool与mongodb在Nodejs中的性能问题
var poolModule = require(‘generic-pool’);
exports.pool = poolModule.Pool({
name : ‘mongo’,
create : function(callback) {
dbutil.createMongoClient(‘mongodbtest’, function(err, db, client){
callback(err,client);
});
},
destroy : function(client) {
client.close();
},
max : 10,
idleTimeoutMillis :30000,
log : false
});.
===============================================
var server_options = {
auto_reconnect: true
},
db_options = {
native_parser: true,
strict: false,
safe: false
};
var mongo = require("mongodb"),
Server = mongo.Server,
var mongoServer = new mongo.Server('192.168.0.2', 27017,server_options);
{
dbName : '',
db : null,
err:null,
createMongoClient : function(dbname, callback){
this.dbName = dbname;
if(this.db == null){
this.db = new mongo.Db(this.dbName,mongoServer , db_options);
}
this.db.open(function(err,db){
this.db = db;
this.err = err;
if(callback) callback(this.err,this.db,dbutil);
});
}
}
为什么上面这个配置,1000并发60秒,会出现请求没有什么响应(之前还出现过: db object already connecting, open cannot be called multiple times);
当我把操作mongodb的代码注释后, 1000并发,30分钟都没有任何问题;
我也测过: max : 1, idleTimeoutMillis :300,
max : 1,
idleTimeoutMillis :3000,
max : 5,
idleTimeoutMillis :300,
等等…
关于generic-pool与mongodb在Nodejs中的性能问题
在Node.js中使用generic-pool
来管理MongoDB客户端连接是一种常见的做法,以确保数据库连接的有效管理和复用。然而,当处理高并发请求时,可能会遇到一些性能瓶颈或错误,例如"db object already connecting, open cannot be called multiple times"。
示例代码分析
首先,让我们看看你提供的示例代码:
// 导入generic-pool模块
var poolModule = require('generic-pool');
// 配置连接池
exports.pool = poolModule.Pool({
name : 'mongo',
create : function(callback) {
dbutil.createMongoClient('mongodbtest', function(err, db, client){
callback(err,client);
});
},
destroy : function(client) {
client.close();
},
max : 10,
idleTimeoutMillis :30000,
log : false
});
// MongoDB客户端配置
var server_options = {
auto_reconnect: true
},
db_options = {
native_parser: true,
strict: false,
safe: false
};
var mongo = require("mongodb");
var Server = mongo.Server;
// 创建MongoDB服务器实例
var mongoServer = new mongo.Server('192.168.0.2', 27017, server_options);
// 定义数据库连接工具类
var dbutil = {
dbName : '',
db : null,
err:null,
createMongoClient : function(dbname, callback){
this.dbName = dbname;
if(this.db == null){
this.db = new mongo.Db(this.dbName, mongoServer, db_options);
}
this.db.open(function(err,db){
this.db = db;
this.err = err;
if(callback) callback(this.err,this.db,dbutil);
});
}
}
性能问题分析
从你的描述来看,在高并发情况下(例如1000并发60秒),你遇到了一些问题,包括请求响应慢或根本没有响应,并且有时会看到错误信息 “db object already connecting, open cannot be called multiple times”。
这些问题可能由以下几个原因引起:
- 连接池配置不当:
max
设置为10,但如果没有正确地关闭或销毁连接,可能会导致连接池耗尽。 - 连接重用问题:在创建新连接时,如果旧连接没有被正确释放或关闭,可能会导致重复连接尝试。
- 数据库操作阻塞:某些数据库操作可能需要较长时间才能完成,导致其他请求等待。
解决方案
- 优化连接池配置:
- 增加
max
值,以允许更多的并发连接。 - 调整
idleTimeoutMillis
,以便更快地回收空闲连接。
- 增加
exports.pool = poolModule.Pool({
name : 'mongo',
create : function(callback) {
dbutil.createMongoClient('mongodbtest', function(err, db, client){
callback(err,client);
});
},
destroy : function(client) {
client.close();
},
max : 20, // 增加最大连接数
idleTimeoutMillis : 10000, // 减少空闲超时时间
log : false
});
- 确保连接正确释放:
- 在每个请求完成后,确保正确关闭数据库连接。
dbutil.createMongoClient('mongodbtest', function(err, db, client){
if (err) {
console.error('Failed to connect to MongoDB:', err);
return;
}
// 执行数据库操作
db.collection('users').find().toArray(function(err, docs) {
// 操作完成后关闭连接
db.close();
});
});
- 避免重复连接尝试:
- 确保在连接过程中不会重复调用
open
方法。
- 确保在连接过程中不会重复调用
通过这些调整,你可以提高连接池的性能和稳定性,减少在高并发情况下的问题。
有两点可疑的。
1.从代码上下文和dbutil这个命名方式来推测,dbutil应该是一个全局性的唯一实例或者它指向的是一个模块。pool的create函数返回的正是dbutil,那么让连接池管理一个全局性实例合适吗。无论create/destroy多少次,用的同一个dbutil里面的db。无论并发多少,能用的数据库连接都只有一个。更糟糕的是,n个请求对同一个db进行open操作(想象n个人争着开同一扇门)。db object already connecting
的警告信息很可能就是由此而来。根据generic-pool的文档,create返回对象一般来说应该是数据库连接本身。。。
2.闭包(Closure)回调函数中this的误用
this.db.open(function(err,db){
this.db = db;
this.err = err;
if(callback) callback(this.err,this.db,dbutil);
});
上面代码中的第一个this和后续的this完全不是同一个对象,虽然它们都叫this。常见的解决方法2个
this.db.open(function(err,db){
this.db = db;
this.err = err;
if(callback) callback(this.err,this.db,dbutil);
}.bind(this));
和
var self=this;
this.db.open(function(err,db){
self.db = db;
self.err = err;
if(callback) callback(self.err,self.db,dbutil);
});
当然,也有可能楼主的意图是在回调函数里面生成一个新对象,然后,然后就不是某家所能臆测的了
dbutil 就是一个mongodb操作的模块,
<<<无论create/destroy多少次,用的同一个dbutil里面的db。无论并发多少,能用的数据库连接都只有一个。更糟糕的是,n个请求对同一个db进行open操作(想象n个人争着开同一扇门)。>>>>
这样会导致数据库连接中有一个?
我有点不疑问,不知道对不对,因为我把下面的参数调成这个后,出现这个问题的现象就当很多了:
max : 50,
idleTimeoutMillis :30000,
把mongodb的find insert 等封装起来,很多人都在这么干啊…
根据你的描述,你遇到了使用 generic-pool
和 MongoDB 在 Node.js 中的性能问题。这可能是因为 MongoDB 客户端在并发连接时出现问题。下面是一些优化建议和示例代码:
1. 确保 MongoDB 客户端正确管理连接
确保每次创建 MongoDB 客户端实例时,不会重复打开连接。你可以通过单例模式来管理 MongoDB 客户端。
2. 调整 generic-pool
配置
你可以调整 generic-pool
的配置参数,如 max
, min
, maxWaitingClients
等,以更好地管理连接池。
示例代码
const genericPool = require('generic-pool');
const MongoClient = require('mongodb').MongoClient;
// 单例模式管理 MongoDB 客户端
let mongoClientInstance;
function getMongoClient() {
if (!mongoClientInstance) {
mongoClientInstance = MongoClient.connect('mongodb://localhost:27017/mongodbtest', { useNewUrlParser: true, useUnifiedTopology: true });
}
return mongoClientInstance;
}
const pool = new genericPool.Pool({
name: 'mongo',
create: async () => {
const client = await getMongoClient();
return client.db();
},
destroy: async (client) => {
await client.close();
},
max: 10,
min: 2,
maxWaitingClients: 10,
acquireTimeoutMillis: 30000,
fifo: true,
log: false
});
module.exports = pool;
解释
- 单例模式:
getMongoClient
函数确保只创建一个 MongoDB 客户端实例。 create
方法:从连接池中获取 MongoDB 客户端实例。destroy
方法:关闭客户端连接。max
,min
,maxWaitingClients
:这些参数控制连接池的最大、最小连接数和等待连接的队列大小。acquireTimeoutMillis
:如果在指定时间内无法获取连接,则抛出错误。
其他注意事项
- 确保 MongoDB 服务器配置正确,并且能够处理高并发连接。
- 监控 MongoDB 性能指标,确保没有瓶颈。
- 使用适当的日志记录来调试和诊断问题。
希望这些示例代码和建议能帮助你解决性能问题。