Entity Framework Core 在睡眠状态下留下许多连接

Sam*_*ulP 5 c# entity-framework entity-framework-core kubernetes asp.net-core

我有一个使用 Entity Framework Core 的 .net 核心 API。数据库上下文在 startup.cs 中注册如下:

  services.AddDbContext<AppDBContext>(options =>
         options.UseSqlServer(connectionString,
         providerOptions => providerOptions.CommandTimeout(60))); 
Run Code Online (Sandbox Code Playgroud)

在我设置的连接字符串中

  Pooling=true;Max Pool Size=100;Connection Timeout=300
Run Code Online (Sandbox Code Playgroud)

控制器调用服务中的方法,服务又调用存储库中的 aysnc 方法以进行数据检索和处理。

如果在负载测试期间并发用户数低于 500,则一切正常。然而,超过这个数字,我开始看到很多超时过期错误。当我检查数据库时,没有死锁,但我可以看到超过 100 个处于睡眠模式的连接(API 托管在两个 kubernetes pod 上)。我在测试过程中监控了这些连接,似乎没有重用当前的睡眠连接,而是将新的连接添加到池中。我的理解是实体框架核心管理打开和关闭连接,但情况似乎并非如此。还是我错过了什么?

错误如下所示:

StatusCode":500,"Message":"Error:Timeout expired. 在从池中获取连接之前超时时间已过。这可能是因为所有池连接都在使用中并且达到了最大池大小。堆栈跟踪:

Microsoft.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 重试, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)\n

Microsoft.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection、DbConnectionFactory connectionFactory、TaskCompletionSource 1 retry, DbConnectionOptions userOptions)\n at Microsoft.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource1 重试、SqlConnectionOverrides 覆盖)\n 在 Microsoft.Data.SqlClient.SqlConnection.Open(SqlConnectionOverrides 覆盖)\n

在 Microsoft.Data.SqlClient.SqlConnection.Open()\n 在

Microsoft.EntityFrameworkCore.Storage.RelationalConnection.OpenInternal(预期布尔错误)\n

Microsoft.EntityFrameworkCore.Storage.RelationalConnection.Open(Boolean errorsExpected)\n 在 Microsoft.EntityFrameworkCore.Storage.RelationalConnection.BeginTransaction(IsolationLevel isolationLevel)\n ..................... ..

如何dbcontext使用的示例:

控制器调用服务类中的方法:

  var result = await _myservice.SaveUserStatusAsync(userId, status);
Run Code Online (Sandbox Code Playgroud)

然后在'myservice'

  var user = await _userRepo.GetUserAsync(userId);

  ....set user status to new value and then

  return await _userRepo.UpdateUserAsync(user);
Run Code Online (Sandbox Code Playgroud)

然后在'userrepo'

  _context.user.Update(user);
   var updated = await _context.SaveChangesAsync();
   return updated > 0;
Run Code Online (Sandbox Code Playgroud)

更新:

非常感谢 Ivan Yang 慷慨地提供了赏金。尽管我仍在调查中,但通过阅读下面的所有评论和答案,我学到了很多东西。这是我迄今为止尝试过的:我将池大小增加到 200(我知道这不是解决问题的正确方法),增加了 Pod 的数量,以便 API 现在可以在 4 个 Pod 上运行并分配更多内存到每个豆荚。到目前为止的最终结果是好的:500 个错误在多达 2000 个并发用户的情况下完全消失。在尝试其他选项后,我将用我的发现更新这个问题。

Dav*_*oft 11

错误:超时已过。从池中获取连接之前超时时间已过。发生这种情况的原因可能是所有池连接都在使用中并且已达到最大池大小。

这几乎总是连接泄漏。在这里,您的查询运行时间很短,并且您看到服务器上有空闲连接,这一事实证实了这一点。您在某个地方留下了开放的连接。

DbContext 将打开/关闭底层连接,并将其返回到 Dispose 池中。但是,如果您在连接上启动事务并且不提交或回滚,则该连接将在池中隔离并且不会被重用。或者,如果您返回的 anIEnumerable或 aDataReader从未被迭代和释放,则无法重用该连接。

查看“睡眠”会话以了解它们的最后一个查询是什么,并将其与您的代码交叉引用以追踪泄漏连接的调用站点。首先尝试DMV,例如

select s.session_id, s.open_transaction_count, ib.event_info
from sys.dm_exec_sessions s
cross apply sys.dm_exec_input_buffer(s.session_id,null) ib
Run Code Online (Sandbox Code Playgroud)

或者如有必要,启动扩展事件跟踪。


小智 -4

尝试关闭池化。池化尝试保持连接打开。如果您将其关闭,它将使用您在 DI 中设置的提供程序超时 60 秒

  • 你明白你在说什么吗? (5认同)