BeginProcessRequest() 中会发生啥?
Posted
技术标签:
【中文标题】BeginProcessRequest() 中会发生啥?【英文标题】:What happens in BeginProcessRequest()?BeginProcessRequest() 中会发生什么? 【发布时间】:2013-06-08 11:59:06 【问题描述】:我们正在使用 NewRelic 提供服务器端应用程序跟踪。
我们注意到我们的一些应用程序在 System.Web.Mvc.MvcHandler.BeginProcessRequest()
方法中始终花费大约 100 毫秒。
这发生在调用任何自定义控制器代码之前(单独记录,而不是累积记录) - 不清楚为什么它会在此方法中花费这么多时间。
MVC 在这种方法中会做什么样的事情?这可能只是请求排队吗?
[编辑:] 怀疑 - Scalayer 在下面的回答是正确的。我们删除并优化了所有会话依赖项,并看到应用程序的可扩展性和稳定性大幅提高
【问题讨论】:
blog.stevensanderson.com/blogfiles/2007/ASPNET-MVC-Pipeline/… @Ravi BeginRequest() 不在那里! :( 您是否使用任何异步处理程序(IHttpAsyncHandler)? 我猜你正在使用异步控制器。 ASP.NET MVC 在幕后做了很多事情。 @CoffeeCode 我们在一些地方使用它们 - 但不在相关页面/痕迹上...... 【参考方案1】:您可能会看到在 .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:查看 >> 输出,然后从“显示输出”下拉菜单中选择“调试”)。
在调试时,点击一个你看过很久的页面。然后查看输出日志 - 如果您看到多个 id,那么您可能会遇到这种情况。
这就是为什么您有时会看到延迟,但有时却看不到,应用程序代码使用会话的方式可能略有不同,或者会话或 IO 操作在页面之间可能会更高或更低。
如果是这种情况,您可以采取一些措施来帮助加快处理速度,具体取决于站点或每个给定页面上使用会话的方式。
对于不修改会话的页面:
<% @Page EnableSessionState="ReadOnly" %>
对于不使用会话状态的页面:
<% @Page EnableSessionState="False" %>
如果应用不使用会话(web.config):
<configuration>
<system.web>
<sessionState mode="Off" />
</system.web>
</configuration>
那么我们来看下面的例子:
用户加载一个页面,然后在第一个请求完成加载之前决定转到另一个页面 ASP.NET 将强制会话锁定,从而导致新页面请求加载等到第一个页面请求完成。使用 ASP.NET MVC,每个操作都会锁定用户会话以进行同步;导致同样的问题。
释放锁所花费的所有时间都将通过 new relic 报告,更不用说用户放弃会话并且返回的线程正在寻找不再存在的用户的时间。
顺便说一句,UpdatePanel 控件会导致相同的行为 -
http://msdn.microsoft.com/en-us/magazine/cc163413.aspx
可以做什么:
这个锁定问题是微软拥有 SessionStateUtility 类的原因之一 -
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionstateutility.aspx
因此,如果您遇到此问题,则可以覆盖默认行为,如此处所示 Redis实现:https://github.com/angieslist/AL-Redis
基于 .net 的网站使用的默认状态提供程序有很多选项。但是通常知道这个事务时间表明线程被锁定并等待对服务器的请求完成。
【讨论】:
感谢您对这个问题的如此出色的回答 - 我有一种强烈的感觉你是正确的,就像在有问题的页面上一样 - 我们很可能会执行大量庞大的会话操作。 我刚试过这个,因为我遇到了类似的问题。当您说“如果您看到多个 id,那么您可能会遇到这种情况”时,您是指同一请求中的多个 ID 吗?还是每个请求都使用不同的 ID? 一个很好的答案,其中很多(以及更多)可以找到in this related question 我们不使用会话,我们已经禁用了它们,但我们仍然看到这个问题。 我知道这是一个老问题,但我和 Doug 一样,同样的问题,没有使用 Sessions!【参考方案2】:如果您希望某个控制器能够并行处理来自单个用户的请求,您可以使用从版本 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
中看到了它。此属性已解决问题并提供了为该控制器并行处理操作的能力。
【讨论】:
【参考方案3】:对于阅读此无法解释的 IIS 高 CPU 使用率的任何人,请查看我打开的 NewRelic 支持论坛中的此线程。
我已经处理这个问题一个多星期了,直到我意识到问题的根本原因是他们的代理。
.NET agent causing high CPU usage on IIS
所以我建议您首先尝试删除 .NET 代理并查看是否有任何变化,然后再进行更复杂的实验。
自从我在处理这个问题时第一次来到这里以来,我就这个特定问题发布了这个答案,线程敏捷性让我走上了一个不正确的漫长过程......(虽然它本身很有趣)。
【讨论】:
【参考方案4】:我在一个会话使用量极少的应用中遇到了同样的问题。考虑到接受的答案,甚至出于诊断目的暂时完全删除 SessionState 后,我仍然在此“方法”中遇到了严重的性能问题
基于 todd_saylor 在this thread 中的评论,我在自己的网站上做了很多研究,发现BeginProcessRequest
可以作为 New Relic 的包罗万象,用于处理不同代码区域中出现的各种性能损失。通过使用 Red Gate 的 ANTs Performance Profiler 在本地机器上分析代码,我能够显着减少这方面的时间。
我的本地分析揭示了一些事情:
-
我使用 Ninject 作为我的 DI 容器,这导致其对象解析的性能损失。我将其替换为 SimpleInjector,并将性能提高了一个数量级。
我发现在 AutoMapper 的 IQueryable Extensions
Project().To<>()
和 Linq 的 Sql Server 提供程序之间的某个地方,预测的动态编译和 JITing 占我请求时间的 50%。将其替换为 CompiledQuerys 带来了另一个重大影响。
希望这对其他人有帮助!
【讨论】:
以上是关于BeginProcessRequest() 中会发生啥?的主要内容,如果未能解决你的问题,请参考以下文章