【发布时间】: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 的类是MultiServerCluster 和SingleServerCluster。正如您通过查看源代码所看到的那样,这些类基本上维护连接池(它们通过使用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);
如果您运行此测试,您将看到equals 和sameCluster 都是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);
如果您运行此测试,您将看到equals 和sameCluster 都是false。
长话短说:
如果您每次都使用完全相同的连接字符串,您可以安全地实例化多个 MongoClient 实例,因为它们都将共享同一个连接池并且您不会杀死您的数据库服务器
【问题讨论】: