MongoDB-MongoClient 驱动程序特有的以下情况下,连接池如何工作?

How does connection pooling work in the following cases specific to MongoDB-MongoClient driver?

连接池是应用程序启动时加载的数据库连接池。当有事务进来时,驱动程序会从连接池中挑选一个连接来为事务服务。当两端通过 tcp 打开套接字以交换消息时,就会发生与数据库的连接。该套接字连接在连接池中时是否处于活动状态?还是从连接池中获取后将启动一个新的套接字连接?。如果是后者,那么连接池中真正存储的是什么?我有一个配置了以下 mongo 值的应用程序:

KeepAlive=true&poolSize=20&autoReconnect=true&socketTimeoutMS=5000&connectTimeoutMS=30000&maxTimeMS=5000

应用程序从一个有 20 个数据库连接的池开始。

如果 sockettimeoutms 是 5000,如果在池中的连接在 5 秒的时间范围内没有被使用,会发生什么情况?还是将 socketTimeoutMs 仅用于事务上下文?

对于 connectTimeoutMS 我相信它是在应用程序尝试创建该套接字连接时使用的。当事务进入时,服务器会尝试创建套接字连接还是已经在池级别完成?

对于 maxTimeMS,我试图通过将其设置为 1 毫秒并尝试使用以下语句向 mongo 发送查询来测试该值:

    @Autowired
    protected MongoTemplate mongoTemplate;

 mongoTemplate.findAll(Myclass.class);

没有失败。但是,当我将 connectTimeout 或 socketTimeout 设置为预期的 1ms 时,它确实失败了。

关于autoReconnect,我没有任何相关信息。我相信它与 KeepAlive 一起使用,以防驱动程序检测到网络问题,它将尝试重新连接。

我在一个项目中使用过Proxool。它运行良好,可配置,AFAIK 稳定。

SourceForge Proxool

有关连接池如何应该 在 MongoDB 驱动程序中工作的非常详细的描述,请参阅 this spec。请记住以下注意事项:

  • 某些驱动程序的现有连接池行为可能与本规范中的要求相矛盾。出于兼容性原因,默认情况下可能会启用此行为。请查阅您的驱动程序文档,看看是否属于这种情况。
  • 不由 MongoDB 维护的驱动程序通常较慢地实现驱动程序规范,因此更可能具有不同的池行为。
  • 本规范主要处理连接,尽管它在某种程度上定义了单个连接的行为,但并未涵盖某些单个连接行为。

A connection to the database happens when both ends open a socket over tcp for exchanging messages

从技术上讲,服务器套接字打开的时间要早​​得多。严格来说,建立连接时只打开客户端套接字。

Will that socket connection be active at the time the connection is in the pool?

是的。除非客户端和服务器之间的连接被操作系统或网络设备断开,并且驱动程序还没有了解到这一点。 TCP keepalive 应该通知驱动程序和服务器发生这种情况,但它不会立即检测到问题。

此外,驱动程序目前没有义务在使用连接之前对连接执行活性检查,因此他们可能会收到错误,因为他们认为连接很好但实际上并不好't。一些驱动程序可能会执行 liveness checking 并且不受此问题的影响。

or a new socket connection will start once picked up from the connection pool?

这是 Ruby 驱动程序中的遗留行为。它不符合当前的 CMAP 规范。

If the latter then what's really stored in the connection pool?

引用连接的轻量级对象,但不是连接到服务器的 TCP 套接字。

If the sockettimeoutms is 5000, what will happen to the connections if they are not used within a timeframe of 5 seconds in the pool?

套接字超时仅在执行操作时适用。它不适用于空闲连接。

发送操作后,驱动程序必须在 5 秒内收到响应,否则操作将因网络错误而失败。

如果操作超过 5 秒,服务器可能正在执行它,但驱动程序将报告网络错误。出于这个原因,低套接字超时通常不是很有用。而是使用 TCP keepalive(如果可能,驱动程序会自动配置)。

or will socketTimeoutMs just be used in a transaction context?

交易与连接无关,您误用了该术语。

For connectTimeoutMS I believe it is used at the time the application attempts to create that socket connection.

没错。

When a transaction comes in, will the server attempt to create a socket connection or will that be already done at the pool level?.

出于当前讨论的目的,不存在“套接字连接”这样的东西。有套接字,也有连接对象。如前所述,服务器不创建套接字。服务器将创建一个与接收到的连接对应的连接对象。

可以说套接字在操作系统级别建立“套接字连接”,但这是在连接池和连接对象之下的级别,因为它们正在本答案中讨论。

For maxTimeMS, I attempted to test this value by setting it to 1 ms and trying to send a query to mongo with the following statement:

两个可能的原因:

  1. 您没有正确设置选项。使用命令监控查看驱动程序发送到服务器的实际命令,验证 maxTimeMS 是否存在并且按照服务器文档使用它。
  2. maxTimeMS 由服务器强制执行。它可能会在查询执行的特定点进行检查,如果查询可以快速执行,它可能在技术上超过指定的超时并成功。参考服务器文档以了解任何注意事项并使用实际的长 运行 查询进行测试(例如,您可以使用 $wheresleep)。

如何在 mongoDB 中使用连接池对象启动 Session()。

我在这里分享代码片段,以在您使用连接池连接数据库时启动 mongoDB 事务会话。

In this I have used global Node.js Object to make connection object available at global scope.


  1. connection.js

const mongoClient = require('mongodb').MongoClient;
global._ = require('underscore')
var url = "mongodb://localhost:27017";

const connectMongo = ()=>{

   return mongoClient.connect(url,{useUnifiedTopology: true ,  poolSize: 10} , (err, client)=>{  
        if(err){
            throw err;
        }
        else{
            var myDB = client.db('myDB');
            
            global.client = client;
            console.log(`connected to ${myDB.databaseName}`);
        }
    });
}

module.exports = connectMongo();
  1. routeController.js

const sendMoney = async (req,res)=>{
    try{
        
        // currently this transaction works for static users
            const session = await global.client.startSession();
            session.startTransaction();
            try {
              const opts = { session, new: true };
              const sender = await Account.
                findOneAndUpdate({ bUser:'60e7f1788377df1114394cb0',bName:"ICICI Bank"}, { $inc: { bAmount: -5 } }, opts);
              if (sender.bAmount < 0) {
                // If A would have negative balance, fail and abort the transaction
                // `session.abortTransaction()` will undo the above `findOneAndUpdate()`
                throw new Error('Insufficient funds: ' + (sender.bAmount + 1));
              }
          
              const reciever = await Account.
                findOneAndUpdate({ bUser: '60e8295cd2c11e2008b37492',bName:"ICICI Bank"}, { $inc: { bAmount: 5 } }, opts);
          
              await session.commitTransaction();
              session.endSession();
              res.send({ from: sender, to: reciever });
            } catch (error) {
              // If an error occurred, abort the whole transaction and
              // undo any changes that might have happened
              await session.abortTransaction();
              session.endSession();
              throw error; // Rethrow so calling function sees error
            }
          
    }
    catch(err){
        throw err;
    }
}