【问题标题】:Is it safe to create multiple instances of MongoClient by using the same connection string each time?每次使用相同的连接字符串创建多个 MongoClient 实例是否安全?
【发布时间】:2019-08-30 23:26:22
【问题描述】:

我试图更好地理解this documentation 关于如何重用MongoClient 的实例。

我们通过在构造函数this is the reference to the constructor overload we call 中传递连接字符串来创建MongoClient 实例。

The official documentation 声明如下(重点是我的):

但是,使用相同设置创建的多个 MongoClient 实例将使用下面的相同连接池。不幸的是,某些类型的设置无法比较是否相等。例如, ClusterConfigurator 属性是一个委托,只有它的地址是已知的用于比较。如果您希望构建多个 MongoClients,如果意图共享连接池,请确保您的代理都使用相同的地址。

我想了解的是,多次调用new MongoClient("mongodb://some-server.net:27017/foo") 是否会使用相同的连接池(请注意,传递给构造函数的连接字符串完全相同构造函数调用)。

我执行了一些测试,通过使用 mongo shell,我使用命令 db.serverStatus().connections 监控连接数。 MongoClient 的单个静态实例切换到创建的多个实例后,连接数没有任何差异

我的直觉是,当使用带字符串的构造函数重载时,文档中突出显示的问题是触发的(当然,只有在每次构造函数时连接字符串都相同时,这才是正确的称为)。文档中描述的与MongoClientSettings 相关的警告可能仅在使用the constructor overload taking an instance of MongoClientSettings 时触发。

仅供参考,这是我的测试代码:

  class Program
  {
    static readonly string[] names = { "Enrico", "Luca", "Mario" };
    static readonly MongoClient client;
    static readonly IMongoDatabase database;
    static readonly IMongoCollection<BsonDocument> collection;

    static Program()
    {
      client = new MongoClient("mongodb://localhost:27017");
      database = client.GetDatabase("test-connections");
      collection = database.GetCollection<BsonDocument>("people");
    }

    static async Task Main(string[] args)
    {
      var tasks = Enumerable
        .Range(1, 2_000_000)
        .AsParallel()
        .Select(_ => WriteDocument());

      await Task.WhenAll(tasks).ConfigureAwait(false);

      Console.WriteLine("All done");

      Console.ReadLine();
    }

    static async Task WriteDocument()
    {
      //var client = new MongoClient("mongodb://localhost:27017");
      //var database = client.GetDatabase("test-connections");
      //var collection = database.GetCollection<BsonDocument>("people");
      var randomIndex = RandomGenerator.Instance.Next(0, names.Length);
      var randomName = names[randomIndex];
      var document = new BsonDocument("name", randomName);
      await collection.InsertOneAsync(document).ConfigureAwait(false);
    }
  }

有谁知道连接池重用在 C# MongoDb 驱动程序中是如何工作的?

2019 年 6 月 30 日更新

从答案中的 cmets 开始(感谢 Haytam 的贡献),我研究了 mongodb C# 驱动程序的代码,似乎给定一个连接字符串,每次都重用相同的连接池。

Here 可以看到MongoClient 构造函数的所有重载都调用了overload taking the MongoClientSetting as parameter

在这个line,您可以看到给定MongoClientSetting 的实例是如何从默认的ClusterRegistry 实例中获得ICluster 的对应实例的。

实现ICluster 的类是MultiServerClusterSingleServerCluster。正如您通过查看源代码所看到的那样,这些类基本上维护连接池(它们通过使用Server 类的实例来实现)。

为了验证两个相同的连接字符串导致两个相同的ICluster 实例,我在单元测试中添加了以下代码(为了运行此单元测试,您必须将其直接添加到 mongodb C# 的源代码中驱动,因为它使用了内部方法ToClusterKey):

  var url1 = new MongoUrl("mongodb://localhost:27017/test");
  var settings1 = MongoClientSettings.FromUrl(url1);
  var clusterKey1 = settings1.ToClusterKey();

  var url2 = new MongoUrl("mongodb://localhost:27017/test");
  var settings2 = MongoClientSettings.FromUrl(url2);
  var clusterKey2 = settings2.ToClusterKey();

  var equals = clusterKey1.Equals(clusterKey2);
  Console.WriteLine(equals);

  var subject = new ClusterRegistry();
  var cluster1 = subject.GetOrCreateCluster(clusterKey1);
  var cluster2 = subject.GetOrCreateCluster(clusterKey2);

  var sameCluster = cluster1.Equals(cluster2);
  Console.WriteLine(sameCluster);

如果您运行此测试,您将看到equalssameCluster 都是true

为了验证不同的连接字符串会导致ICluster 的不同实例,您可以运行以下单元测试,其中第二个连接字符串已通过指定自定义连接超时(默认连接超时为 30 秒)进行了修改:

  const string connString1 = "mongodb://localhost:27017/test";
  var settings1 = MongoClientSettings.FromConnectionString(connString1);
  var clusterKey1 = settings1.ToClusterKey();

  const string connString2 = "mongodb://localhost:27017/test?connectTimeoutMS=15000";
  var settings2 = MongoClientSettings.FromConnectionString(connString2);
  var clusterKey2 = settings2.ToClusterKey();

  var equals = clusterKey1.Equals(clusterKey2);
  Console.WriteLine(equals);

  var subject = new ClusterRegistry();
  var cluster1 = subject.GetOrCreateCluster(clusterKey1);
  var cluster2 = subject.GetOrCreateCluster(clusterKey2);

  var sameCluster = cluster1.Equals(cluster2);
  Console.WriteLine(sameCluster);

如果您运行此测试,您将看到equalssameCluster 都是false

长话短说:

如果您每次都使用完全相同的连接字符串,您可以安全地实例化多个 MongoClient 实例,因为它们都将共享同一个连接池并且您不会杀死您的数据库服务器

【问题讨论】:

    标签: c# mongodb


    【解决方案1】:

    所有的构造函数最终都会创建一个设置对象:

    MongoClient(string connectionString) 调用MongoClient(MongoUrl url),后者调用MongoClient(MongoClientSettings settings)

    在此处查看源代码:https://github.com/mongodb/mongo-csharp-driver/blob/master/src/MongoDB.Driver/MongoClient.cs#L105

    line 79_cluster = ClusterRegistry.Instance.GetOrCreateCluster(_settings.ToClusterKey()); 负责知道您正在使用相同的设置并将使用相同的集群,我猜它使用相同的连接池。

    【讨论】:

    • 源代码中的好东西。我们必须研究ClusterKey (github.com/mongodb/mongo-csharp-driver/blob/…) 的相等语义,以确保集群重用(注意对GetOrCreateCluster 的调用)。但这是进一步调查的良好起点。
    • 为了更好地澄清我的评论,我认为您发现的代码行有助于更好地了解如何重用连接,但您不能假设每次都使用同一个集群。这取决于如何比较 ClusterKey 类型的对象。另一件要研究的事情是 MongoClientSettings 在传递连接字符串时是如何由 MongoClient 构造函数构造的(关键点是在 ClusterKey 中通过引用比较的委托和集合属性)
    • 他们重写 Equals 方法来比较每个属性。不确定更改属性是否是个好主意(因为它们已被使用),ApplicationName 呢?
    • 为什么不使用同一行创建 2 个设置对象并查看它们是否相等?这样你就肯定知道了。
    • 我可能会通过将单元测试添加到驱动程序存储库的本地副本来做到这一点。那就是说这些东西应该有更好的记录
    猜你喜欢
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 2011-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-22
    • 1970-01-01
    相关资源
    最近更新 更多