无法将IIS/Asp.NET配置为同时处理多个异步请求
我正在尝试探索异步ASP.NET请求。Asp.NET应用程序由IIS 8托管 客户端正在使用以下代码发出许多POST请求:无法将IIS/Asp.NET配置为同时处理多个异步请求,asp.net,.net,iis,asynchronous,concurrency,Asp.net,.net,Iis,Asynchronous,Concurrency,我正在尝试探索异步ASP.NET请求。Asp.NET应用程序由IIS 8托管 客户端正在使用以下代码发出许多POST请求: private static async Task<Result> IOAsync(Uri url, byte[] body) { var webClient = new WebClient(); webClient.Headers["Content-Type"] = "application/json"; return Deserial
private static async Task<Result> IOAsync(Uri url, byte[] body)
{
var webClient = new WebClient();
webClient.Headers["Content-Type"] = "application/json";
return DeserializeFromBytes(await webClient.UploadDataTaskAsync(url, "POST", body));
}
private static Result DeserializeFromBytes(byte[] bytes)
{
using (var jsonTextReader = new JsonTextReader(new StreamReader(new MemoryStream(bytes))))
{
return new JsonSerializer().Deserialize<Result>(jsonTextReader);
}
}
sampleapiaasync
旨在探索实现数据库IO的不同方法,其中只有一种是真正异步的,其余的则是为了证明关于使用Task.Run
和类似方法“模拟”数据库IO的常见误解
在我的特定场景中,IOAsync
方法是真正异步的:
private async Task<Result> IOAsync(string sqlPauseDuration)
{
Result result;
using (var conn = new SqlConnection(m_connectionString))
using (var cmd = CreateCommand(conn, sqlPauseDuration))
{
await conn.OpenAsync();
using (var reader = await cmd.ExecuteReaderAsync())
{
await reader.ReadAsync();
result = new Result(reader.GetDateTime(0), reader.GetGuid(1));
}
}
return result;
}
private SqlCommand CreateCommand(SqlConnection conn, string sqlPauseDuration)
{
const string SQL = "WAITFOR DELAY @Duration;SELECT GETDATE(),NEWID()";
var sqlCommand = new SqlCommand(SQL, conn) { CommandTimeout = QueryTimeout };
sqlCommand.Parameters.Add(new SqlParameter("Duration", sqlPauseDuration));
return sqlCommand;
}
在sql server上,持续时间为20秒的@不会产生任何峰值,并且需要5分钟(几乎完全如此)!从中我得出结论,请求的处理并没有足够的并发性。如果我猜的话,我会说它同时处理(5*60)/20=15个请求
请注意:
- 在进行测试之前,我有“冷运行”请求来预热IIS和Sql Server。因此,启动时间对方程没有影响李>
- 当我使用相同的设置直接从客户端运行
IOAsync
时——600个请求加20秒,我确实看到了Sql Server上线程计数的预期峰值,并且所有600个请求在不到21秒的时间内同时完成李>
由此我得出结论,问题出在Asp.NET/IIS方面
通过谷歌搜索该问题,提示我更改文件C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Aspnet.config和C:\Windows\Microsoft.NET\Framework\v4.0.30319\Aspnet.config,如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="false" />
<legacyImpersonationPolicy enabled="true"/>
<alwaysFlowImpersonationPolicy enabled="false"/>
<SymbolReadingPolicy enabled="1" />
<shadowCopyVerifyByTimestamp enabled="true"/>
</runtime>
<startup useLegacyV2RuntimeActivationPolicy="true" />
<system.web>
<applicationPool maxConcurrentRequestsPerCPU="5000" maxConcurrentThreadsPerCPU="0" requestQueueLimit="5000"/>
</system.web>
</configuration>
响应
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 19 Nov 2015 17:37:15 GMT
Content-Length: 83
{"Id":"16e8c3a2-fc95-446a-9459-7a89f368e074","Timestamp":"\/Date(1447954635240)\/"}
编辑2
请在下面找到Asp.Net应用程序的web.config:
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.5.2" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.5.2"/>
<httpRuntime targetFramework="4.5"/>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
这与
现在,我已经部署到Windows Server 2012上,在那里我可以发出75个DB请求,每个请求长10秒,几乎总共10秒完成。但不是76,我从中得出结论,实际并发限制是75。不是5000。仍在寻找线索
编辑6
根据的建议,我已将所有DB IO替换为Task.Delay
,并停止Sql Server
如果没有asp.net,我可以轻松运行600任务。延迟10秒,所有任务都会在10秒内结束(只需要一点点额外的时间)
在asp.net中,结果是一致的——75个请求是完全并发和异步的。除此之外,情况有所不同。因此,80个请求耗时16秒,100个请求耗时20秒,200个请求耗时30秒。对我来说,很明显,请求是由Asp.NET或IIS限制的,就像以前执行DB IO时一样。我猜这是由于客户端“每个端点最多两个并发连接”规则造成的,这是由原始HTTP规范强制执行的
您需要设置适当的值以允许更多并发连接
例如:(在发送请求之前在客户端执行此操作)
这将允许您的应用程序打开无限(实际上)数量的并发HTTP连接
以下引述自:
单用户客户端与任何服务器或代理的连接不应超过2个[…]这些准则旨在提高HTTP响应时间并避免拥塞
因此,兼容客户端通常必须确保其与单个服务器的开放连接不会超过两个
但是,这已经改变,您可以覆盖此行为。以下引用是对HTTP/1.1协议的更新:
以前的HTTP版本将特定数量的连接作为
上限,但对于许多应用来说,这是不切实际的。
因此,本规范未规定特定的最大值
连接数,但鼓励客户端
在打开多个连接时,请保守
在一天结束时,在遵循给出的建议后,我找到了罪犯。令人惊讶的是,是Fiddler阻止了这些请求!我想有一个小提琴手在某处控制着它
我通过检查请求统计信息中的ClientConnected
vsClientBeginRequest
时间戳发现了它。虽然ClientConnected
是相同的-当客户端发送请求时,它有不同的ClientBeginRequest
-在一定数量的请求之后,它开始延迟,延迟逐渐增加。如果您在Windows 8上而不是在Server 2012上运行IIS 8.0,有些东西有硬编码的限制。您应该能够将其增加到默认值以上(Windows 8 Professional的默认值限制为10,Basic edition的默认值限制为3):.
因此,在applicationHost.config的
部分(通常位于C:\Windows\System32\inetsrv\config中),您可以添加
元素,如本文末尾所示:
<sites>
<site name="Default Web Site" id="1" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="C:\inetpub\wwwroot" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:" />
</bindings>
</site>
<site name="X" id="2">
<application path="/" applicationPool="X">
<virtualDirectory path="/" physicalPath="C:\Inetpub\wwwroot\X" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:X" />
</bindings>
<traceFailedRequestsLogging enabled="false" />
<limits maxConnections="40" />
</site>
<siteDefaults>
<logFile logFormat="W3C" directory="%SystemDrive%\inetpub\logs\LogFiles" />
<traceFailedRequestsLogging directory="%SystemDrive%\inetpub\logs\FailedReqLogFiles" />
</siteDefaults>
<applicationDefaults applicationPool="DefaultAppPool" />
<virtualDirectoryDefaults allowSubDirConfig="true" />
</sites>
最大可用值为40
使用IIS管理器选择网站,然后更改“高级设置…”->“限制”->“最大并发连接数”中的值可能更安全
如果您需要更多并发连接,一个选项是获得Windows Server 2012的试用版,并在虚拟机(Hyper-V)中运行。我建议将与开发平台相对应的服务器版本(如server 2012 R2)与Windows 8.1配合使用,这样您就可以在这两个平台上发现相同的问题。ASP.NET会话状态如何?我不知道如何检查它。添加
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/json; charset=utf-8
Server: Microsoft-IIS/8.0
X-AspNetMvc-Version: 5.2
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Thu, 19 Nov 2015 17:37:15 GMT
Content-Length: 83
{"Id":"16e8c3a2-fc95-446a-9459-7a89f368e074","Timestamp":"\/Date(1447954635240)\/"}
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0"/>
<add key="webpages:Enabled" value="false"/>
<add key="ClientValidationEnabled" value="true"/>
<add key="UnobtrusiveJavaScriptEnabled" value="true"/>
</appSettings>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.5.2" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.5.2"/>
<httpRuntime targetFramework="4.5"/>
</system.web>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed"/>
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35"/>
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2"/>
<httpRuntime targetFramework="4.5"/>
<sessionState mode="Off" />
</system.web>
PS C:\> &"C:\Program Files\IIS Express\appcmd.exe" list config -section:system.applicationHost/sites |sls limits | group
Count Name Group
----- ---- -----
30 <limits /> { <limits />, <limits />, <limits />, <limits />...}
PS C:\>
NumberOfConcurrentRequests = TotalRequests / NumberOfBatches
= TotalRequests / (TotalTime / OneRequestDuration)
= (TotalRequests / TotalTime) * OneRequestDuration
= (600 / 300) * 5
= 10
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
<sites>
<site name="Default Web Site" id="1" serverAutoStart="true">
<application path="/">
<virtualDirectory path="/" physicalPath="C:\inetpub\wwwroot" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:" />
</bindings>
</site>
<site name="X" id="2">
<application path="/" applicationPool="X">
<virtualDirectory path="/" physicalPath="C:\Inetpub\wwwroot\X" />
</application>
<bindings>
<binding protocol="http" bindingInformation="*:80:X" />
</bindings>
<traceFailedRequestsLogging enabled="false" />
<limits maxConnections="40" />
</site>
<siteDefaults>
<logFile logFormat="W3C" directory="%SystemDrive%\inetpub\logs\LogFiles" />
<traceFailedRequestsLogging directory="%SystemDrive%\inetpub\logs\FailedReqLogFiles" />
</siteDefaults>
<applicationDefaults applicationPool="DefaultAppPool" />
<virtualDirectoryDefaults allowSubDirConfig="true" />
</sites>