关于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,

等等…


4 回复

关于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”。

这些问题可能由以下几个原因引起:

  1. 连接池配置不当max 设置为10,但如果没有正确地关闭或销毁连接,可能会导致连接池耗尽。
  2. 连接重用问题:在创建新连接时,如果旧连接没有被正确释放或关闭,可能会导致重复连接尝试。
  3. 数据库操作阻塞:某些数据库操作可能需要较长时间才能完成,导致其他请求等待。

解决方案

  1. 优化连接池配置
    • 增加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
});
  1. 确保连接正确释放
    • 在每个请求完成后,确保正确关闭数据库连接。
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();
    });
});
  1. 避免重复连接尝试
    • 确保在连接过程中不会重复调用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;

解释

  1. 单例模式getMongoClient 函数确保只创建一个 MongoDB 客户端实例。
  2. create 方法:从连接池中获取 MongoDB 客户端实例。
  3. destroy 方法:关闭客户端连接。
  4. max, min, maxWaitingClients:这些参数控制连接池的最大、最小连接数和等待连接的队列大小。
  5. acquireTimeoutMillis:如果在指定时间内无法获取连接,则抛出错误。

其他注意事项

  • 确保 MongoDB 服务器配置正确,并且能够处理高并发连接。
  • 监控 MongoDB 性能指标,确保没有瓶颈。
  • 使用适当的日志记录来调试和诊断问题。

希望这些示例代码和建议能帮助你解决性能问题。

回到顶部