首页 > 解决方案 > 使用(托管)后台服务时的 .NET Core / EF Core (SQL) Max Pool 问题

问题描述

我和我的团队在高利用率的 .NET Core Web 应用程序上遇到了一些 EF Core / SQL 池问题。

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

当托管服务 (QueuedHostedService) 的后台任务中引用 EF Core / ApplicationDbContext 时,问题开始出现。遵循以下指南:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

我遵循了推荐的依赖注入和托管服务指南。

重现步骤

相关线路:

启动.cs

...
services.AddDbContext<ApplicationDbContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

...

services.AddScoped<IScopedWorker, ScopedWorker>();
services.AddSingleton<MySingletonThatAddsToBackground>();
// Follow the host service guide from microsoft.
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();

MySingletonThatAddsToBackground.cs
public class MySingletonThatAddsToBackground
{
    private readonly IBackgroundTaskQueue _taskQueue;
    private readonly ILogger _logger;
    private readonly CancellationToken _cancellationToken;
    public IServiceProvider _services { get; }

    public MySingletonThatAddsToBackground(IServiceProvider services, IBackgroundTaskQueue taskQueue,
        ILogger<MonitorLoop> logger,
        IHostApplicationLifetime applicationLifetime)
    {
        _services = services;
        _taskQueue = taskQueue;
        _logger = logger;
        _cancellationToken = applicationLifetime.ApplicationStopping;
    }

    public void DoWorkBackground()
    {
        // Enqueue a background work item
        _taskQueue.QueueBackgroundWorkItem(async token =>
        {
            try
            {
                using (var scope = _services.CreateScope())
                {
                    var scopedWorker = scope.ServiceProvider.GetRequiredService<IScopedWorker>();

                    await scopedWorker.DoWork();
                }
            }
            catch (OperationCanceledException)
            {
                // Prevent throwing if the Delay is cancelled
            }
        });
    }
}
ScopedWorker.cs
public class ScopedWorker : IScopedWorker
{
    private readonly ApplicationDbContext _db;

    public ScopedWorker(ApplicationDbContext db)
    {
        _db = db;
    }

    public void DoWork()
    {
        var customers = _db.MyCustomers.ToListAsync();

        // Do stuff to customers.

        await _db.SaveChangesAsync();
    }
}

讨论

在MySingletonThatAddsToBackground里面,using (var scope)完成后,不应该释放范围,然后释放ScopedWorker(作用域),然后释放ApplicationDbConext(作用域),然后关闭连接/池连接吗?

是否有什么我没有正确实施导致连接池泄漏?

更多技术细节

EF Core 版本:3.1.4 数据库提供程序:Microsoft.EntityFrameworkCore.SqlServer 目标框架:.NET Core 3.1.4 操作系统:Windows Server 2016、SQL Server 2016 IDE:16.6

标签: entity-frameworkasp.net-core.net-coreentity-framework-coreef-core-3.0

解决方案


我最近阅读了这篇文章(在 ASP.NET Monsters),我认为这对您的情况有所帮助:请尝试使用NETSTAT并查看State每个请求的监控后台服务;在文章中包含更多解释的细节,但是,在恢复时,问题与垃圾处理例程中的错误行为相关联,这会影响 HTTP 管道建立的结束。

换句话说,检查 (by NESTAT) ifState是否不同于ESTABLISHED; 如果发生这种情况,您将找到根本原因,并且需要更改您的代码方法,小心使用垃圾收集。


推荐阅读