C# MongoDB客户端引发等待队列已满异常
我看到了一个奇怪的问题,MongoDB的.NET客户端抛出了一个C# MongoDB客户端引发等待队列已满异常,c#,mongodb,multithreading,C#,Mongodb,Multithreading,我看到了一个奇怪的问题,MongoDB的.NET客户端抛出了一个等待队列,等待获取到服务器127.0.0.1:27017的连接,队列已满。异常 我有一个大小为10的信号灯来保护对MongoDB的任何调用。 这意味着,对Mongo的并发调用永远不会超过10个 .NET驱动程序的默认连接池大小为100,大于10。 因此,10个并发调用不应该是一个问题 为了复制这一点,我有下面的代码,是的,但它使问题变得明显 我还为MongoDB找到了这个规范 这有关系吗? 是否每个调用线程(在本例中为线程池工作线
等待队列,等待获取到服务器127.0.0.1:27017的连接,队列已满。
异常
我有一个大小为10的信号灯来保护对MongoDB的任何调用。
这意味着,对Mongo的并发调用永远不会超过10个
.NET驱动程序的默认连接池大小为100,大于10。
因此,10个并发调用不应该是一个问题
为了复制这一点,我有下面的代码,是的,但它使问题变得明显
我还为MongoDB找到了这个规范
这有关系吗?
是否每个调用线程(在本例中为线程池工作线程)都进入等待队列并尝试获取连接,如果我有更多的工作线程,即使并发级别较低,也必须将连接分配给这个新的调用工作线程
using System;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Driver;
using System.Threading;
namespace ConsoleApp58
{
public class AsyncSemaphore
{
private readonly SemaphoreSlim _semaphore;
public AsyncSemaphore(int maxConcurrency)
{
_semaphore = new SemaphoreSlim(
maxConcurrency,
maxConcurrency
);
}
public async Task<T> WaitAsync<T>(Task<T> task)
{
await _semaphore.WaitAsync();
//proves we have the correct max concurrent calls
// Console.WriteLine(_semaphore.CurrentCount);
try
{
var result = await task;
return result;
}
finally
{
_semaphore.Release();
}
}
}
class Program
{
public class SomeEntity
{
public ObjectId Id { get; set; }
public string Name { get; set; }
}
static void Main(string[] args)
{
var settings = MongoClientSettings.FromUrl(MongoUrl.Create("mongodb://127.0.0.1:27017"));
// settings.MinConnectionPoolSize = 10;
// settings.MaxConnectionPoolSize = 1000;
// I get that I can tweak settings, but I want to know why this occurs at all?
// if we guard the calls with a semaphore, how can this happen?
var mongoClient = new MongoClient(settings);
var someCollection = mongoClient.GetDatabase("dummydb").GetCollection<SomeEntity>("some");
var a = new AsyncSemaphore(10);
// is this somehow related ?
// https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#id94
_ = Task.Run(() =>
{
while (true)
{
// this bit is protected by a semaphore of size 10
// (we will flood the thread pool with ongoing tasks, yes)
_ = a.WaitAsync(RunTask(someCollection))
//after the task is done, dump the result
// dot is OK, else exception message
.ContinueWith(x =>
{
if (x.IsFaulted)
{
Console.WriteLine(x.Exception);
}
});
}
}
);
Console.ReadLine();
}
private static async Task<SomeEntity> RunTask(IMongoCollection<SomeEntity> pids)
{
//simulate some mongo interaction here
var res = await pids.Find(x => x.Name == "").FirstOrDefaultAsync();
return res;
}
}
}
使用系统;
使用System.Threading.Tasks;
使用MongoDB.Bson;
使用MongoDB.Driver;
使用系统线程;
名称空间控制台EAPP58
{
公共类异步信号量
{
私有只读信号量limu信号量;
公共异步信号量(int-maxConcurrency)
{
_信号量=新信号量lim(
maxConcurrency,
最大并发
);
}
公共异步任务WaitAsync(任务任务)
{
wait_信号量。WaitAsync();
//证明我们有正确的最大并发调用数
//Console.WriteLine(_semaphore.CurrentCount);
尝试
{
var结果=等待任务;
返回结果;
}
最后
{
_semaphore.Release();
}
}
}
班级计划
{
公共类实体
{
公共对象Id{get;set;}
公共字符串名称{get;set;}
}
静态void Main(字符串[]参数)
{
var settings=MongoClientSettings.FromUrl(MongoUrl.Create(“mongodb://127.0.0.1:27017"));
//settings.MinConnectionPoolSize=10;
//settings.MaxConnectionPoolSize=1000;
//我知道我可以调整设置,但我想知道为什么会发生这种情况?
//如果我们用信号量来保护呼叫,怎么会发生这种情况?
var mongoClient=新的mongoClient(设置);
var someCollection=mongoClient.GetDatabase(“dummydb”).GetCollection(“some”);
var a=新的异步信号量(10);
//这有什么关系吗?
// https://github.com/mongodb/specifications/blob/master/source/connection-monitoring-and-pooling/connection-monitoring-and-pooling.rst#id94
_=任务。运行(()=>
{
while(true)
{
//此位受大小为10的信号量保护
//(我们将用正在进行的任务淹没线程池,是的)
_=a.WaitAsync(RunTask(someCollection))
//任务完成后,转储结果
//dot正常,否则显示异常消息
.ContinueWith(x=>
{
如果(x.IsFaulted)
{
Console.WriteLine(x.Exception);
}
});
}
}
);
Console.ReadLine();
}
专用静态异步任务RunTask(IMongoCollection pids)
{
//在这里模拟一些mongo交互
var res=await pids.Find(x=>x.Name==“”)。FirstOrDefaultAsync();
返回res;
}
}
}
建立连接需要时间。您不会立即获得100个可用连接。如果您创建一个客户机并立即请求10个操作,而没有可用的连接,则可以点击等待队列超时
一些驱动程序也有等待队列长度限制。它不是标准化的,我认为应该弃用它,但出于兼容性的原因,它可能会继续存在。请查阅驾驶员文档,了解如何提升
然后,增加waitQueueTimeoutMS或逐渐增加负载,或者在启动负载之前等待建立连接(后者可以使用CMAP事件)
确保由10个未完成操作组成的并发边界也能正常工作