使用Azure SQL和Azure AppServices忽略最大池大小

时间:2018-02-09 19:23:12

标签: asp.net-web-api2 azure-sql-database connection-pooling azure-web-app-service azure-app-service-plans

我正在使用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个数据库会话。

此时我想到了几个选项可以解释这种奇怪的行为:

  • 请记住,连接池是一个属于客户端的概念,我认为第一件事是IIS应用程序池中的某种参数是这种行为的原因。
  • 另一种选择是每个主机进程和数据库登录只能打开128个会话。我没有在互联网上找到任何指向此内容的东西..但在其他数据库如Oracle中,可以配置此约束以限制每次登录打开的会话数量。
  • 最后一个选项..在一些博客和stackoverflow线程中我已经读过我们正在接收的异常(在从池中获取连接之前已经过了超时时间。这可能是因为所有池连接都在使用中而且已经发生了达到最大池大小)可能会产生误导,并存在其他问题导致异常的可能性..

快速解决方案是禁用连接字符串中的池,但这是我要做的最后一件事。

另一个解决方案是扩展AppService以添加更多VM实例,但这在金钱方面是昂贵的。

任何人都知道Azure AppServices中是否存在某种限制,这解释了为什么在启用连接池时只打开了128个会话?

2 个答案:

答案 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