我正在使用ASP.NET Web API项目(完整的.NET Framework 4.6.1)并使用Azure SQL数据库,该API部署在Azure AppService上。关于服务层,我们在Azure SQL数据库(50 DTU)的情况下使用S2,在AppService的情况下使用B1,其中部署了API端点(1个核心和1.75 GB的RAM)。此时我们正在使用2个实例(2个带负载均衡器的VM)
我们的质量保证团队正试图在性能方面找出平台的容量。他们使用JMeter配置了性能测试,包括在60秒的间隔内启动4000个请求。
首次执行性能测试后,HTTP 500错误的比例非常高,在查看了日志后,我们发现了很多例外情况:
System.InvalidOperationException: 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.
at System.Data.Common.ADP.ExceptionWithStackTrace(Exception e)
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass4.<<ExecuteAsync>b__3>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<ExecuteAsyncImplementation>d__9`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Data.Entity.Core.EntityClient.EntityConnection.<OpenAsync>d__8.MoveNext()
我想到的第一件事就是连接泄漏问题,我们正在审查代码,并使用sp_who2命令监视SQL Server上的连接,但是连接按预期处理。
我们正在使用每次必须处理新请求时创建实体框架上下文(查询都是异步)的注入容器,当请求结束时(实例化依赖项),实体框架上下文会自动处理。
我们得出的结论是,我们需要增加连接池的大小,以减少流量负载较大的场景中的超时。
在互联网上快速搜索我发现Max Pool Size值的默认值为100:
https://www.connectionstrings.com/all-sql-server-connection-string-keywords/
我决定将值增加到400:
Server=tcp:XXXX.database.windows.net,1433;Initial Catalog=XXXX;Persist Security Info=False;User ID=XXXX;Password=XXXXXXXXXXXX;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Max Pool Size=400;
在重复性能测试之后,我们感到惊讶的是,我们没有注意到任何改进,因为我们收到了相同比例的HTTP 500错误。我们得出的结论是,最大池大小被忽略了。
我们接下来要做的是在性能测试期间监视SQL Server,以便找出每个主机进程打开了多少个会话,此时我们正在使用以下SQL语句:
SELECT
COUNT(*) AS sessions,
host_name,
host_process_id,
program_name,
DB_NAME(database_id) AS database_name
FROM
sys.dm_exec_sessions AS s
WHERE
(is_user_process = 1) AND
(program_name = '.Net SqlClient Data Provider')
GROUP BY host_name, host_process_id, program_name, database_id
ORDER BY sessions DESC
在每个主机进程(部署API端点的虚拟机)监视打开的会话之后,我们发现每个虚拟机只创建了128个数据库会话。
此时我想到了几个选项可以解释这种奇怪的行为:
快速解决方案是禁用连接字符串中的池,但这是我要做的最后一件事。
另一个解决方案是扩展AppService以添加更多VM实例,但这在金钱方面是昂贵的。
任何人都知道Azure AppServices中是否存在某种限制,这解释了为什么在启用连接池时只打开了128个会话?
答案 0 :(得分:2)
连接池超时通常是症状而不是原因。如果您已达到120 maximum concurrent requests on S2的约束,则在数据库中投入更多连接/查询将无法提高吞吐量。您抛出的其他工作将排队,包括新的连接请求。我不确定在实际达到最大大小之前是否会导致连接池超时错误。
构建JMeter测试的方式可能会影响结果。出于容量测试的目的,确保QA执行相当缓慢的上升,直到达到错误SLA。您也可以尝试移动到S3或更高版本以查看是否可以缓解连接问题。
答案 1 :(得分:0)
每个https://docs.microsoft.com/en-us/azure/sql-database/sql-database-dtu-resource-limits-elastic-pools的50个DTU的每个池的最大并发工作者(请求)为 100