无法配置IIS / Asp.NET同时处理多个异步请求多个、IIS、Asp、NET

2023-09-06 10:13:05 作者:£飞天★凤凰彡

我想探索异步ASP.NET请求。该Asp.NET应用程序是由IIS 8主办。

I am trying to explore the asynchronous ASP.NET requests. The Asp.NET application is hosted by IIS 8.

客户端使用以下code正在发出许多POST请求:

The client side is issuing many POST requests using the following code:

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);
    }
}

的Asp.NET code 的另一面是在VS 2013新建项目向导与在 HomeController中稍作修改创建的Asp.NET Web应用程序

The Asp.NET code on the other side is the Asp.NET Web application created by the VS 2013 New Project Wizard with a slight modification in the HomeController:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";

        return View();
    }

    public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";

        return View();
    }

    [System.Web.Mvc.HttpPost]
    public async Task<JsonResult> SampleAPIAsync([FromBody] BaseContext c, Runner.BasicIOStrategy strategy)
    {
        return Json(await ((IAsyncRunner)Runner.Create((Runner.IOStrategy)strategy)).IOAsync(c));
    }
}

SampleAPIAsync 是为了探索不同的方法做数据库IO,其中只有一个是真正的异步,其余的都是有展示有关模拟通常的误解它使用 Task.Run 和类似。

The SampleAPIAsync is meant to explore different approaches to do database IO, of which only one is truly asynchronous, the rest are there to demonstrate the usual misconceptions about "simulating" it using Task.Run and similar.

在我的特殊情况下的 IOAsync 方法是真正的异步:

In my particular scenario the IOAsync method is truly asynchronous:

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;
}

所以,你可以看到,一切都是异步的。我使用性能监视器来检查线程数的:

So, as you can see, everything is asynchronous. I am using the Performance Monitor to check the Thread Count on:

在客户端应用程序 的IIS工作进程(W3wp.exe) 在SQL Server进程

我的期望是看到一个相对平坦的线为客户端和IIS工作进程和突然飙升SQL服务器研究。

My expectation is to see a relatively flat line for the client and the IIS worker process and a sudden spike in the Sql Server.

这从未发生过。做600请求最终转换到运行

This never happens. Doing 600 requests ultimately translating to running

WAITFOR DELAY @Duration;SELECT GETDATE(),NEWID()

在SQL服务器的 @Duration 20秒不产生任何尖峰,仅需5分钟(几乎一模一样)!从中我得出结论的请求没有被处理的足够的并发性。如果我猜,我会说这处理(5 * 60)/ 20 = 15的请求兼任。

on the sql server with the @Duration of 20 seconds does not generate any spikes and takes 5 minutes (almost exactly)! From which I conclude the requests are not being processed with the sufficient concurrency. If I am to guess, I would say it processes (5 * 60) / 20 = 15 requests concurrently.

请注意,即:

在此之前的测试中,我有冷运行请求热身IIS和SQL Server。因此,启动时间不向方程。 当我运行 IOAsync 直接从使用相同的设置客户端 - 600请求20秒我也看到了SQL Server上的线程数的预期穗所有600请求完成后,在少于21秒同时! Before doing the test I have "cold run" requests to warm up the IIS and the Sql Server. So, the startup time does not contribute to the equation. When I run the IOAsync directly from the client using the same setup - 600 requests and 20 seconds I do see the expected spike of the Thread Count on the Sql Server and all of the 600 requests complete at the same time after less than 21 second!

这是我总结的问题是关于Asp.NET/IIS侧。

From which I conclude the problem is on the Asp.NET/IIS side.

谷歌搜索的问题,促使我改变文件的 C:\ WINDOWS \ Microsoft.NET \ Framework64 \ v4.0.30319 \ Aspnet.config 和 C:\ WINDOWS \ Microsoft.NET \框架\ v4.0.30319 \ Aspnet.config 是这样的:

Googling the issue prompted me to change the files C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Aspnet.config and c:\Windows\Microsoft.NET\Framework\v4.0.30319\Aspnet.config like this:

<?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>

注意在 /configuration/system.web/applicationPool 元素。这样做不影响结果。 - 同时仍然明显15请求

Notice the /configuration/system.web/applicationPool element. Doing so does not affect the outcome - still apparent 15 requests at the same time.

我是什么做错了吗?

修改1

不知道这是否是相关的,这是多么典型的HTTP请求和响应时,看通过提琴手观察到这样的:

Not sure if it is relevant, this is how a typical HTTP request and response look like when observed through Fiddler:

请求

POST http://canws212:12345/Home/SampleAPIAsync?strategy=Async HTTP/1.1
Content-Type: application/json
Host: canws212:12345
Content-Length: 174
Expect: 100-continue

{"IsBlocking":false,"Strategy":0,"Server":"CANWS212","Count":600,"QueryTimeout":30,"DurationSeconds":20,"ConnectionTimeout":4,"DBServer":"localhost","DBName":"tip_DFControl"}

响应

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如下:

Please, find below the web.config of the Asp.Net application:

<?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>

修改3

关掉的sessionState 在web.config:

Turned off the sessionState in the web.config:

<system.web>
  <compilation debug="true" targetFramework="4.5.2"/>
  <httpRuntime targetFramework="4.5"/>
  <sessionState mode="Off" />
</system.web>

没有爱心,同样的结果。

No loving, same result.

修改4

经过了&LT;限制&GT; 元素,它是默认的:

Checked the <limits> element, it is the default:

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:\>

所以,我想我不会限制它人为地。该INETMGR报告如下:

So, I suppose I am not limiting it artificially. The InetMgr reports the following:

在另一方面我运行Windows 8与IIS 8,这样的 http://weblogs.asp.net/owscott/windows-8-iis-8-concurrent-requests-limit 应适用。我尝试到了极限既是站点默认值,并在我的网站更改为5000,但它并没有承担任何成果 - 同样的结果

On the other hand I am running Windows 8 with IIS 8, so http://weblogs.asp.net/owscott/windows-8-iis-8-concurrent-requests-limit should apply. I tried to change the limit to 5000 both as site defaults and on my site, but it did not bear any fruits - same result.

我所做的是:

在使用INETMGR GUI相关网站更改限制为5000。 IISRESET 运行我的测试

同样的结果。

修改5

我的JSON体反序列化code有一个错误 - 它使用的默认值的持续时间,而不是一个在请求主体。所以,这不是20秒,不过5秒的持续时间每个SQL语句。我使用的公式是错误的,应该是

My JSON body deserialization code has a bug - it used the default value for the duration, rather than the one in the request body. So, it was not 20 seconds, but 5 seconds duration per SQL statement. And the formula I used is wrong, it should be

NumberOfConcurrentRequests = TotalRequests / NumberOfBatches 
                           = TotalRequests / (TotalTime / OneRequestDuration)
                           = (TotalRequests / TotalTime) * OneRequestDuration
                           = (600 / 300) * 5
                           = 10

这与 HTTP一致://博客。 asp.net/owscott/windows-8-iis-8-concurrent-requests-limit

现在,我已经部署到Windows Server 2012中有我能发出75分贝的要求,每次10秒长而完整的几乎完全10秒一起。但不是76,我从中得出的实际并发限制是75不是5000仍在寻找线索。

Now, I have deployed to a Windows Server 2012 and there I am able to issue 75 DB requests, each 10 seconds long which complete in almost exactly 10 seconds all together. But not 76, from which I conclude the actual concurrency limit is 75. Not 5000. Still looking for the clues.

修改6

继斯蒂芬·克利的建议我已经更换了所有数据库IO与任务.Delay 并停止SQL服务器。

Following the suggestion of Stephen Cleary I have replaced all the DB IO with Task.Delay and stopped the Sql Server.

没有asp.net我可以轻松跑600 Task.Delay 10秒,所有的人都停止在10秒(带一点点小的额外)。

Without asp.net I can easy run 600 Task.Delay of 10 seconds and all of them end in 10 seconds (with a little tiny extra).

使用asp.net的结果是一致的 - 75的要求是完全同步和异步。以上情况表明,情况就不同了。因此,80请求用了16秒100的请求了20秒和200花了30秒。很明显,我认为这些请求节流无论是Asp.NET或IIS,当该数据库IO被行使,就像以前一样。

With asp.net the result is consistent - 75 requests are fully concurrent and asynchronous. Above that, the picture is different. So, 80 requests took 16 seconds, 100 requests took 20 seconds and 200 took 30 seconds. It is obvious to me that the requests are throttled either by Asp.NET or IIS, just as before when the DB IO was exercised.

推荐答案

如果你正在运行IIS 8.0在Windows 8上,而不是Server 2012中,有一些东西硬codeD的限制。您应该能够增加它的默认以上(默认限制将是10为Windows 8专业版,3基础版)的默认限制为Web网站,也的的Windows 8 / IIS 8并发请求限制。

If you're running IIS 8.0 on Windows 8 rather than Server 2012, there are hard-coded limits on some things. You should be able to increase it above the default (the default limit would be 10 for Windows 8 Professional, 3 for Basic edition): Default Limits for Web Sites , also Windows 8 / IIS 8 Concurrent Requests Limit‌​.

所以,在&LT; system.applicationHost&GT; 的applicationHost.config的部分(通常位于C:\ WINDOWS \ SYSTEM32 \ INETSRV \配置),你可以添加&LT;限制&GT; 元素,如图靠近本月底:

So, in the <system.applicationHost> section of applicationHost.config (normally located in C:\Windows\System32\inetsrv\config) you can add a <limits> element as shown near the end of this:

<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最大可用值。

With a maximum usable value of 40.

这可能是更安全的使用IIS管理器来选择网站,然后更改高级设置...的价值 - >限制 - >最大并发连接

It could be safer to use IIS Manager to select the web site then change the value in "Advanced settings..."->"Limits"->"Maximum Concurrent Connections".

如果您需要更多的并发连接,其中一个方案是让Windows Server 2012中的试用版,并在虚拟机(Hyper-V的)运行它。我建议得到其对应于开发平台的服务器版本,例如服务器2012 R2去与Windows 8.1,让你找到两个相同的问题。

If you have a need for more concurrent connections, one option would be to get a trial version of Windows Server 2012 and run it in a virtual machine (Hyper-V). I suggest getting the server version which corresponds to the development platform, e.g. Server 2012 R2 to go with Windows 8.1, so that you find the same problems on both.