在MVC控制器的操作方法中使用async / await可以扩展Web应用程序,因为等待Asp.Net线程池的请求线程被释放,以便它可以处理IIS队列中的此工作进程的其他请求.这意味着如果我们将工作进程的队列长度限制为10,并向异步操作发送50-100个请求,则IIS不应返回HTTP 503错误,因为始终存在从Asp.Net线程池到服务器的空闲线程来电请求.
我有一个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;
}
}
此操作方法只是在将总和结果返回给调用代码之前延迟给定的秒数.非常基本的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会在await之前和之后使用不同的线程.
我想要证明的是,使用async / await将扩展Web应用程序.
解决方法:
我认为你混淆游泳池队列. ASP.NET请求有5个位置可以在IIS服务器上排队.
>应用程序池队列
> IIS工作进程
> CLR线程池队列
>集成模式全局队列
>经典模式应用程序队列
您设置为10的队列长度是HTTP.SYS:应用程序池队列.
当您使用async / awat时,您正在使用ASP.NET:CLR线程池队列.
这就是为什么即使使用async / await也会出现503错误的原因.
另一方面,here there is a wonderful article关于scalling web应用程序与async / await可以帮助你.
[编辑]我刚刚发现this article关于请求排队也可以帮助.