BeginProcessRequest()会发生什么?

时间:2013-06-12 11:31:45

标签: c# asp.net asp.net-mvc-3 newrelic

我们正在使用NewRelic来提供服务器端应用程序跟踪。

我们注意到,我们的一些应用程序始终在方法System.Web.Mvc.MvcHandler.BeginProcessRequest()中花费大约100毫秒。

这在调用任何自定义控制器代码之前发生(单独记录,而不是累积记录) - 为什么在这种方法上花费这么多时间并不明显。

MVC在这种方法中会做些什么?这可能只是请求排队吗?

[编辑:] 如所怀疑的 - Scalayer在下面的回答是正确的。我们删除了&优化了我们所有的会话依赖性,并看到了应用程序可扩展性的大幅增加。稳定性

4 个答案:

答案 0 :(得分:85)

您可能会看到的内容通常被称为.NET中的线程敏捷性。

对于主题标签下的结果(即System.Web.HttpApplication.BeginRequest()中的应用程序代码),您可能会看到的是线程敏捷性问题;在大多数情况下,您在此处看到的时间不一定是正在执行的代码,而是等待线程从读写器锁定释放回来的Web上下文。

Application_BeginRequest()“暂停”是一个在ASP.NET Web堆栈中非常普遍的“暂停”。通常,当您在BeginRequest中看到长加载时间时,您正在处理ASP.NET线程敏捷性和/或线程锁定 - 尤其是在处理基于IO和会话的操作时。这不是一件坏事,这就是.net确保你的线程保持并发的方式。

时间间隔通常发生在BeginRequest和PreRequestHandlerExecute之间。如果应用程序正在向会话写入多个内容,那么ASP.NET将在HttpContext.Current.Session上发出读写器锁。

查看这是否是您可能遇到的问题的好方法是检查线程ID以查看敏捷性是否存在问题 - 对于给定的请求,ID将是不同的。

例如。在调试时,您可以将以下内容添加到Global.asax.cs

protected void Application_BeginRequest(Object sender, EventArgs e) { 
      Debug.WriteLine("BeginRequest_" + Thread.CurrentThread.ManagedThreadId.ToString()); 
   }

打开调试输出窗口(从Visual Studio:View>>输出,然后从“show output from”下拉列表中选择“Debug”。

在调试时,点击您已经看过很长时间的页面。然后查看输出日志 - 如果你看到多个id,那么你可能会受此影响。

这就是为什么你有时会看到延迟但不是其他时候,应用程序代码可能会稍微使用会话,或者会话或IO操作可能在页面之间更高或更低。

如果是这种情况,您可以采取一些措施来帮助加快速度,具体取决于网站或每个指定网页上的会话使用方式。

对于不修改会话的页面:

   <% @Page EnableSessionState="ReadOnly" %>

对于不使用会话状态的页面:

<% @Page EnableSessionState="False" %>

如果应用不使用会话(web.config):

<configuration>
    <system.web>
      <sessionState mode="Off" />
    </system.web>
</configuration>

让我们看一下以下例子:

用户加载页面,然后决定在第一个请求完成之前转到另一个页面加载ASP.NET将强制会话锁定导致新页面请求加载等待第一个页面请求完成。使用ASP.NET MVC,每个操作都会锁定用户会话以进行同步;导致同样的问题。

锁定释放所花费的所有时间都将通过新文件进行报告,更不用说用户放弃会话并且回来的帖子正在寻找不再存在的用户。

顺便提一下,UpdatePanel控件会导致相同的行为 -

http://msdn.microsoft.com/en-us/magazine/cc163413.aspx

可以做些什么:

此锁定问题是Microsoft具有SessionStateUtility类的原因之一 -

http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx

如果您遇到此问题,可以覆盖默认行为,如此处所示 Redis实施:https://github.com/angieslist/AL-Redis

.net网站使用的默认状态提供程序有很多选项。但是通常知道这个事务时间表明线程正在被锁定并等待对服务器的请求完成。

答案 1 :(得分:6)

如果要使某个控制器能够从单个用户并行处理请求,则可以使用自版本3以来在MVC中引入的名为SessionState的属性。没有必要使用无会话控制器以便实现并行性,SessionStateBehavior的Ready-only选项将允许您通过基于会话数据实现的安全检查。

[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
public class ParallelController : Controller
{
    ...
}

我在System.Web.Mvc.MvcHandler.BeginProcessRequest()时也有延迟,当我尝试做一些长时间运行的动作时,我在NewRelic中看到了它。此属性解决了问题,并为处理此控制器的并行操作提供了能力。

答案 2 :(得分:2)

我在一个使用非常少的会话的应用程序中遇到了同样的问题。在将接受的答案考虑在内,甚至暂时删除SessionState以进行诊断之后,我仍然在这个方法中遇到了重大问题&#39;方法&#39;

根据todd_saylor在this thread中的评论,我在自己的网站上进行了大量研究,发现BeginProcessRequest可以作为New Relic的全部内容出现在不同代码区域的各种性能损失。通过使用Red Gate的ANT Performance Profiler在本地计算机上分析代码,我能够显着缩短这一领域的时间。

我的本​​地分析显示了一些事情:

  1. 我使用Ninject作为我的DI容器,导致其对象分辨率的性能下降。我用SimpleInjector替换它并将性能提高了一个数量级。
  2. 我发现在AutoMapper的IQueryable Extensions Project().To<>()和Linq的Sql Server提供程序之间,投影的动态编译和JITing占我请求时间的50%。用CompiledQuerys取而代之的是另一个重大缺陷。
  3. 希望这有助于其他人!

答案 3 :(得分:0)

对于任何读过这个体验IIS高CPU使用率的人来说,无法解释,请看看我开的NewRelic支持论坛中的这个帖子。

我已经处理了这个问题超过一个星期,直到我意识到这是他们的代理人才是问题的根本原因。

.NET agent causing high CPU usage on IIS

所以我建议您首先尝试删除.NET代理,看看是否有任何更改,然后再进行更复杂的实验。

我在这个特定问题上发布了这个答案,因为我在处理这个问题时首先来到这里,线程敏捷性让我走上了一条不正确的长路线......(虽然它本身非常有趣)。