我们有一个网站,目前正在与并发用户进行斗争。
以下是该项目的高层次背景:
SubmitSearch控制器上的Search操作。平均响应时间为5-10秒。因此,正如第二点所概述的,我们不想花太多的时间在这个项目上重写大的部分。但是,我们希望尝试增加并发用户。我们不打算改变任何其他东西或提高性能,因为这将需要更多的工作。
我们所看到的是,随着越来越多的人访问SubmitSearch,网站总体上会放缓。这很可能是因为所有IIS线程都被锁定,执行搜索。
我们希望实现AsyncController,并使SubmitSearch操作在普通CLR线程上执行。下面是我们想要实现的方法:
假设这是原始的SubmitSearch方法:
/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
public virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
//our search code
}我们希望转换为AsyncController的最快方法是简单地这样做:
/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
protected virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
//our search code
}
/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
AsyncManager.OutstandingOperations.Increment();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
ActionResult result = SubmitSearch(searchData);
AsyncManager.Parameters["result"] = result;
AsyncManager.OutstandingOperations.Decrement();
});
return;
}
/// <summary>
/// Called when the asynchronous search has completed
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public ActionResult SubmitSearchCompleted(ActionResult result)
{
//Just return the action result
return result;
}当然,这不起作用,因为贯穿整个代码,我们引用的是HttpContext.Current,在这种方法中,我们知道它最终是null。
所以我们希望用SubmitSearchAsync来做这件事
/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
AsyncManager.OutstandingOperations.Increment();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
ActionResult result = null;
AsyncManager.Sync(() =>
{
result = SubmitSearch(searchData);
});
AsyncManager.Parameters["result"] = result;
AsyncManager.OutstandingOperations.Decrement();
});
return;
}这解决了这个问题。
所以我担心的是:
在SubmitSearch方法中包装AsyncManager.Sync的执行是否辜负了使用该模型的目的?换句话说,当我们在AsyncManager.Sync方法中时,我们是否回到了IIS线程,这使我们回到了第一步?
谢谢
发布于 2013-11-09 03:33:16
在
SubmitSearch方法中包装AsyncManager.Sync的执行是否辜负了使用该模型的目的?换句话说,当我们在AsyncManager.Sync方法中时,我们是否回到了IIS线程,这使我们回到了第一步?
或多或少,是的。但不幸的是,在您的示例中,使用Task.Factory.StartNew 也使无法使用异步控制器。用你想要的方法,你赢不了。
IIS线程(由ThreadPool.QueueUserWorkItem启动的线程和Task线程)都来自同一个线程池。
为了从异步控制器中获得任何好处,您需要真正的异步方法。换句话说,像Stream.ReadAsync或WebRequest.GetResponseAsync这样的方法。这些特别命名的方法使用I/O完成端口而不是普通线程,后者使用硬件中断并在不同的线程池上操作。
很久以前,我在这里的回答中提到了这一点:在高流量情况下在ThreadPool.QueueUserWorkItem中使用ASP.NET。任务和服务生是非常贴心的,但它们不会改变.NET线程池的基本动态。
需要注意的一件事是,有一个选项,TaskCreationOptions.LongRunning,您可以在创建Task时指定它,这实际上告诉框架任务将做大量等待,理论上,TPL将试图避免在线程池中调度它。实际上,这在高流量站点上可能不太实用,因为:
new Thread --如果不是字面意义上的,那么至少是有效的。这意味着大量的上下文切换,这绝对会降低性能,也是线程池首先存在的主要原因。"search“命令显然意味着某种I/O,这意味着您可能可以在某个地方使用真正的异步方法,即使它是旧风格的BeginXyz/EndXyz。这里没有快捷方式,也没有快速修复;您必须重新构建代码才能真正实现是异步的。
.NET框架无法检查Task内部发生了什么,并神奇地将其转换为中断。它不能使用I/O完成端口,除非直接引用知道它们的特定方法。
接下来,您使用的web或中间件应用程序,试着提前考虑这一点,避免像瘟疫一样的同步I/O。
发布于 2013-11-09 04:49:13
我认为,到目前为止,阿罗瑞格有着最好的答案:你需要真正的异步处理来进行扩展(例如,Begin/End,而不仅仅是使用线程池线程),而且异步代码没有快捷方式或快速修复--至少需要对该部分进行重新设计。
这让我想到你问题的这一部分:
我们不想花太多的时间在这个项目重写大的部分。
最好的好处可能是购买更多的内存,并把它放在服务器上。(您应该先检查分析器,以确保它是内存问题-内存通常是ASP.NET的限制因素,但最好先检查一下)。
虽然我们开发人员喜欢解决问题,但事实是,我们可以消耗大量时间,例如,将同步代码转换为异步代码。FYI,基于任务的异步模式(async/await)在.NET 4.5中将允许您更容易地将同步代码更改为异步代码。
所以现在,我说买几个内存芯片,然后在你切换到async 4.5之后,做一个思想上的笔记,把它升级到.NET上(容易得多)。
发布于 2013-11-07 05:51:22
首先,我将查看服务器本身的性能,然后考虑使用visual中的分析工具来准确地确定瓶颈所在。考虑查看迷你分析器,其中的讨论可以在这里找到http://www.hanselman.com/blog/NuGetPackageOfTheWeek9ASPNETMiniProfilerFromStackExchangeRocksYourWorld.aspx。一般同意上面关于线程消耗的评论。
https://stackoverflow.com/questions/19327011
复制相似问题