今天,我想模仿等待在MVC3 Web角色的AsyncController内长期运行的阻塞进程(5至30秒)。 然而,开始,我刚开始用1秒,得到的东西去。 是的,这种智慧是值得商榷的,因为阻塞操作,目前无法对异步的I / O完成端口到外部服务运行,但我想看看性能极限是什么,这种特殊的情况。
在我的Web角色,我部署了6个个小例子。 唯一的控制器是一个AsyncController,具有两个用于模拟一个1000毫秒阻塞操作的简单方法。
该MVC3 Web角色控制器是简单:
public class MessageController : AsyncController
{
public void ProcessMessageAsync(string id)
{
AsyncManager.OutstandingOperations.Increment();
Task.Factory.StartNew(() => DoSlowWork());
}
public ActionResult ProcessMessageCompleted()
{
return View("Message");
}
private void DoSlowWork()
{
Thread.Sleep(1000);
AsyncManager.OutstandingOperations.Decrement();
}
}
接下来,我施加的应力从亚马逊EC2 Web角色。 使用12台服务器,我憋足负荷慢慢向上,并得到了接近550个请求/秒。 推超出了任何企图遭到了明显的线程饥饿和后续错误。 我认为我们打的CLR线程限制,我的理解是每CPU 100个线程。 搞清楚一些开销为AsyncController,平均每秒六分之五百五十零= 92请求每个服务器为1000毫秒阻塞操作的似乎符合该结论。
这是真的吗? 我看到其他人说类似的事情,在那里他们达到了每例如每秒60至80请求与该类型的负载。 此系统上的负荷将主要由长时间运行的操作,每秒92所以在请求1000毫秒会一路下跌时,5000个毫秒任务联机。
短通过多个单独的web角色前路由为阻塞I / O请求的结束扇该负载到更多的核,是否有任何方式比在1000毫秒块时间每秒90名或这样的请求的这种明显的限制以获得更高的? 因为我已某种这里明显的错误呢?
对不起,我不得不说这买,你已经被所有的博客,声称只要使用误导Task.Factory.StartNew
是解决所有的问题,那么,它不是。
与Task.Factory.StartNew负载测试
采取以下负载测试我对你的代码做了一下(我改变了睡眠10秒,而不是1秒,使事情变得更糟)。 该测试模拟了200个常数用户共做2500个的请求。 而看看有多少失败的请求存在由于线程不足:
正如你所看到的,即使你使用的是与任务的AsyncController,线程饥饿仍在进行之中。 难道是因为长时间运行的过程中引起的?
与TaskCreationOptions.LongRunning负载测试
你知道,如果一个任务是长时间运行或者不是你可以指定? 看看这个问题: 奇怪的行为。当我不使用TaskCreationOptions.LongRunning
当你不使用LongRunning标志,任务计划在线程池线程,而不是它自己的(专用)线程。 这可能是你的行为变化的原因- 当你没有LongRunning标志在原地踏步,你可能得到线程池饥饿由于在过程中的其他线程。
让我们来看看,如果我们改变1行代码会发生什么:
public void ProcessMessageAsync(string id)
{
Task.Factory.StartNew(DoSlowWork, TaskCreationOptions.LongRunning);
AsyncManager.OutstandingOperations.Increment();
}
看看负载测试,有什么区别!
刚刚发生了什么?
正如你所看到的,LongRunning选择似乎有很大的不同。 让我们添加一些记录,看看内部发生:
public void ProcessMessageAsync(string id)
{
Trace.WriteLine(String.Format("Before async call - ThreadID: {0} | IsBackground: {1} | IsThreadPoolThread: {2} | Priority: {3} | ThreadState: {4}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground,
Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.Priority, Thread.CurrentThread.ThreadState));
Task.Factory.StartNew(DoSlowWork, TaskCreationOptions.LongRunning);
AsyncManager.OutstandingOperations.Increment();
}
...
private void DoSlowWork()
{
Trace.WriteLine(String.Format("In async call - ThreadID: {0} | IsBackground: {1} | IsThreadPoolThread: {2} | Priority: {3} | ThreadState: {4}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsBackground,
Thread.CurrentThread.IsThreadPoolThread, Thread.CurrentThread.Priority, Thread.CurrentThread.ThreadState));
Thread.Sleep(10000);
AsyncManager.OutstandingOperations.Decrement();
}
无LongRunning:
Before async call - ThreadID: 11 | IsBackground: True | IsThreadPoolThread: True | Priority: Normal | ThreadState: Background
Async call - ThreadID: 11 | IsBackground: True | IsThreadPoolThread: True | Priority: Normal | ThreadState: Background
随着LongRunning:
Before async call - ThreadID: 48 | IsBackground: True | IsThreadPoolThread: True | Priority: Normal | ThreadState: Background
Async call - ThreadID: 48 | IsBackground: True | IsThreadPoolThread: False | Priority: Normal | ThreadState: Background
正如你所看到的,没有LongRunning你实际上是使用从线程池中的线程,从而导致饥荒。 虽然LongRunning选项,在这种情况下的伟大工程,你必须不断的评估 ,如果你真的需要它。
注:由于您使用的Windows Azure,你需要考虑到负载平衡器将闲置几分钟后超时。