async / await 如何帮助 ASP.Net 应用程序?

How async / await can help in ASP.Net application?

在 MVC 控制器的 action 方法中使用 async / await 可以扩展 Web 应用程序,因为在 await 时 Asp.Net 线程池的请求线程被释放,以便它可以处理 IIS 队列中的其他请求这个工作进程。这意味着如果我们将工作进程的队列长度限制为 10,并向异步操作发送 50 - 100 个请求,IIS 不应 return HTTP 503 错误,因为总会有一个来自 [=24= 的空闲线程] 线程池来处理传入的请求。

我有一个 WebApi 可以进行如下计算:

public class ValuesController : ApiController
{
    public int GetSum(int x, int y, int timeDelay = 1)
    {
        Thread.Sleep(timeDelay*1000);
        int result = x + y;
        return result;
    }
}

此操作方法只是在 return 将求和结果传递给调用代码之前延迟给定的秒数。非常基本的 Web api 只是为了模仿长时间运行的代码。

接下来是等待结果的 MVC 异步操作:

public class ThreadStarvationController : Controller
{
    public async Task<ActionResult> CallGetSumWithDelayAsync(int num1 = 5, int num2 = 8, int timeDelay = 60)
    {
        int callingThreadId = Thread.CurrentThread.ManagedThreadId;
        ThreadStarvationModel model = new ThreadStarvationModel();

        string url = "http://localhost:8111/api/values/GetSum?x=" + num1 + "&y=" + num2 + "&timeDelay=" + timeDelay;

        using (HttpClient client = new HttpClient())
        {

            // here still using the same request thread...
            // following line will force await to free up the request thread and wait asynchronouly               //for the response.
            model.ResponseFromWebService = await client.GetStringAsync(url);

            // here we can be on the same request thread or anyother thread... more likely on //another other thread than 
            // request thread.
        }

        int returningThreadId = Thread.CurrentThread.ManagedThreadId;

        model.CallingThreadId = callingThreadId;
        model.ReturningThreadId = returningThreadId;

        return this.View(model);
    }
}

WebApi 和 MVC 托管在 IIS 上。 MVC 网站被限制在队列中只有 10 个请求。

当客户端在 15 或 20 个请求后调用 MVC 异步方法时,IIS 发送 HTTP 503 错误,这意味着 IIS 队列已满请求。

这是调用 MVC 异步方法的控制台应用程序代码。它调度 30 个任务并并行执行它们。

          List<Task> taskList = new List<Task>();
        for (int x = 0; x < 30; x++)
        {
            string url = "http://localhost:8333/ThreadStarvation/CallGetSumWithDelayAsync?num1=" + x + "&num2=" + x + "&timeDelay=1";

            Task stringDataTask = new Task(() =>
            {
                using (HttpClient webClient = new HttpClient())
                {
                    string data = webClient.GetStringAsync(url).Result;
                    Console.WriteLine("{0}", data);
                }
            });


            taskList.Add(stringDataTask);
        }

        DateTime startTime = DateTime.Now;

        Parallel.ForEach(taskList, item => item.Start());

        Console.WriteLine("================== START {0} ===========================", startTime);

        Task.WaitAll(taskList.ToArray());
        DateTime endTime = DateTime.Now;
        Console.WriteLine("================= THE END {0} ============================", endTime);

运行时,在大约 20 个请求后,我收到 HTTP 503 错误消息。

如果我使用同步 MVC 动作,结果还是一样。我知道异步/等待在等待之前和之后使用不同的线程。

我想证明的是,使用 async / await 将扩展 Web 应用程序。

我认为您混淆了池队列。有 5 个地方 ASP.NET 请求可以在 IIS 服务器上排队。

  1. 应用程序池队列
  2. IIS 工作进程
  3. CLR 线程池队列
  4. 集成模式全局队列
  5. 经典模式应用队列

您设置为 10 的队列长度是 HTTP.SYS:应用程序池队列。

当您使用 async/awat 时,您正在使用 ASP.NET:CLR 线程池队列。

这就是为什么即使使用 async/await 也会出现 503 错误的原因。

另一方面,here there is a wonderful article 关于使用 async/await 缩放 Web 应用程序可以帮助您。

[编辑] 我刚刚发现 this article 关于请求排队也有帮助。