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 服务器上排队。
- 应用程序池队列
- IIS 工作进程
- CLR 线程池队列
- 集成模式全局队列
- 经典模式应用队列
您设置为 10 的队列长度是 HTTP.SYS:应用程序池队列。
当您使用 async/awat 时,您正在使用 ASP.NET:CLR 线程池队列。
这就是为什么即使使用 async/await 也会出现 503 错误的原因。
另一方面,here there is a wonderful article 关于使用 async/await 缩放 Web 应用程序可以帮助您。
[编辑] 我刚刚发现 this article 关于请求排队也有帮助。
在 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 服务器上排队。
- 应用程序池队列
- IIS 工作进程
- CLR 线程池队列
- 集成模式全局队列
- 经典模式应用队列
您设置为 10 的队列长度是 HTTP.SYS:应用程序池队列。
当您使用 async/awat 时,您正在使用 ASP.NET:CLR 线程池队列。
这就是为什么即使使用 async/await 也会出现 503 错误的原因。
另一方面,here there is a wonderful article 关于使用 async/await 缩放 Web 应用程序可以帮助您。
[编辑] 我刚刚发现 this article 关于请求排队也有帮助。