BeginProcessRequest()会发生什么?

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

我们已经注意到,我们的一些应用程序一直在System.Web.Mvc.MvcHandler.BeginProcessRequest()方法中花费大约100ms。

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

MVC会在这种方法中做什么样的事情? 难道这只是请求排队?

[编辑:] 怀疑 – Scalayer的答案是下面点。 我们删除并优化了所有的会话依赖关系,并且看到了应用程序可伸缩性和稳定性的大幅提升

你可能会看到在.NET中通常被称为线程敏捷性。

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

Application_BeginRequest() “暂停”是在ASP.NET Web堆栈中相当普遍的一种。 通常,当您在BeginRequest中看到很长的加载时间时,您正在处理ASP.NET线程敏捷性和/或线程锁 – 特别是在处理IO和基于会话的操作时。 这不是一件坏事,这只是.net如何确保你的线程保持并发。

时间间隔通常发生在BeginRequest和PreRequestHandlerExecute之间。 如果应用程序正在写入几个会话,则ASP.NET将在HttpContext.Current.Session上发出读写器locking。

一个很好的方法来看看,如果这是一个问题,你可能会面临的问题是检查线程ID,看看是否敏捷是一个问题 – 对于一个给定的请求ID将是不同的。

例如。 在debugging的时候,也许你可以把下面的代码添加到你的Global.asax.cs

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

打开debugging输出窗口(从Visual Studio:View >> Output,然后从“show output from”下拉列表中select“Debug”)。

在debugging时,打一个你已经看了很久的页面。 然后查看输出日志 – 如果你看到多个ID,那么你可能正在遭受这种痛苦。

这就是为什么有时可能会看到延迟,而其他时候可能会看到延迟,应用程序代码可能会略有不同,或者会话或IO操作可能会更高或更低。

如果是这种情况,您可以根据网站或每个给定页面上的会话使用情况来做一些事情来加快速度。

对于不修改会话的页面:

  <% @Page EnableSessionState="ReadOnly" %> 

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

 <% @Page EnableSessionState="False" %> 

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

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

那么我们来看下面的例子:

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

所有释放锁的时间都将通过新文档进行报告,更不用说用户放弃会话的时间,而返回的线程正在查找不再存在的用户。

顺便说一句,UpdatePanel控件导致相同的行为 –

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

可以做什么:

这个locking问题是Microsoft有SessionStateUtility类的原因之一 –

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

所以如果你面对这个问题,你可以重写默认的行为,就像在这个Redis实现中看到的一样: https : //github.com/angieslist/AL-Redis

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

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

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

我也有延迟System.Web.Mvc.MvcHandler.BeginProcessRequest() ,当试图做一些长时间运行的行动,我看到它在NewRelic 。 这个属性已经解决了问题,并赋予了处理这个控制器并行操作的能力。

我在一个会话使用非常less的应用程序中遇到了同样的问题。 考虑到接受的答案,甚至暂时删除SessionState用于诊断的目的,我仍然有这个“方法”性能的重大问题,

基于todd_saylor在这个主题中的评论,我在自己的网站上做了大量的研究,发现BeginProcessRequest可以作为New Relic的全部function,用于在不同代码区出现的各种性能损失。 通过使用Red Gate的ANT性能分析器在我的本地机器上分析代码,我能够显着缩短这方面的时间。

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

  1. 我使用Ninject作为我的DI容器,导致其对象分辨率的性能损失。 我用SimpleInjector取代了它,性能提高了一个数量级。
  2. 我发现在AutoMapper的IQueryable Extensions Project().To<>()和Linq的Sql Server提供程序之间,dynamic编译和投影JITing占了我请求时间的50%。 用CompiledQuerys取代了另一个重大的缺陷。

希望这可以帮助别人!

对任何人阅读这经历IIS高CPU使用率是不明原因的,看看这个线程从我打开NewRelic支持论坛。

我一直在处理这个问题一个多星期,直到我意识到这是他们的代理问题的根本原因。

.NET代理在IIS上导致高CPU使用率

所以我build议你尝试的第一件事就是删除.NET代理,看看是否有任何改变,然后再进入更复杂的实验。

我在这个问题上发表了这个问题,因为在这个问题上我第一次来到这里,而且线程敏捷性让我在一个长期的过程中不正确…(尽pipe本身非常有趣)。