Concurrency MVC应用程序并发使用时的堆栈空问题

Concurrency MVC应用程序并发使用时的堆栈空问题,concurrency,asp.net-mvc-4,thread-safety,renderaction,Concurrency,Asp.net Mvc 4,Thread Safety,Renderaction,我们最近开发了一个新的ASP.NET MVC4 web应用程序(C#/Visual Studio)。在本地测试和调试之后,我们将其部署到生产环境中,然后开始收到越来越多的健康监控邮件。它们有不同的异常消息: 堆栈为空 收集被修改;枚举操作不能执行 项目已添加。字典中的键:'ALL_HTTP'正在添加的键:'ALL_HTTP'(还提到了其他键) 值不在预期范围内 例如,我们无法简单地解决或再现一系列错误类型。“Stack Empty”是发生次数最多的一个,每天发生100次(例如,对于1-10%

我们最近开发了一个新的ASP.NET MVC4 web应用程序(C#/Visual Studio)。在本地测试和调试之后,我们将其部署到生产环境中,然后开始收到越来越多的健康监控邮件。它们有不同的异常消息:

  • 堆栈为空
  • 收集被修改;枚举操作不能执行
  • 项目已添加。字典中的键:'ALL_HTTP'正在添加的键:'ALL_HTTP'(还提到了其他键)
  • 值不在预期范围内
例如,我们无法简单地解决或再现一系列错误类型。“Stack Empty”是发生次数最多的一个,每天发生100次(例如,对于1-10%的用户),因此我们将重点放在这一个,因为其他错误似乎是相关的。以下是部分堆栈跟踪:

Exception information:
Exception type: System.InvalidOperationException
Exception message: Stack empty.
...
Stack trace:    at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.Stack`1.Pop()
at System.Web.WebPages.TemplateStack.Pop(HttpContextBase httpContext)
如图所示,堆栈跟踪大部分完全位于MVC框架(System.Web)中。我们自己的代码中唯一经常出现在某些堆栈跟踪中的地方是请求URL的视图(.cshtml文件),然后是@Html.RenderAction()调用。到目前为止,我们已经重构了很多这样的函数来调用RenderPartial()。这导致堆栈跟踪中没有更多视图,尽管一些渲染部分现在也提供了一些视图

搜索此错误表明并发/并行执行是原因。这与我们最初无法在本地重现错误的事实相符,但它确实发生在生产中。我们没有进行负载测试,但现在已经能够通过同时启动大量应用程序/请求在本地开发人员系统上重现错误。但是,在我们的代码中,没有使用显式并行指令执行任何操作

这似乎与MVC视图不是线程安全的有关。然而,很难想象没有其他人会遇到这种情况。我们每天有几千名访客,随时都有大约30多名活跃用户。可悲的是,由于谷歌排名下降(与这个问题相关),这个数字正在下降


有人知道这个问题的解决方案吗?

我正在开发一个ASP.NET MVC 4应用程序,我还遇到了您提到的错误。虽然它们是不同的,但它们似乎有相同的来源。在花了几个小时试图找到原因(以及大量代码更改)之后,我从头开始了分析

因为我使用的是一个自定义路由,并且该路由有一个处理程序,它可以检查多个内容,还可以访问我通过注释数据库访问开始的数据库。快速打开几个浏览器选项卡(使用IISExpress>Show All Application window或按住Ctrl键并单击链接),我很高兴看到所有页面都正确显示,而不是显示几个随机错误消息。尝试了几次以确定,并得出结论,在访问数据库时出现了问题

public class MyNewRouteHandler : IRouteHandler {

    IHttpHandler MvcHandler;

    public IHttpHandler GetHttpHandler(RequestContext requestContext) {
        MvcHandler = new MvcHandler(requestContext);

        // some checkings and
        // some database access code 
        // that was commented

        return MvcHandler;
    }
}
一位同事建议我在这个方法中添加一个小线程睡眠:
GetHttpHandler
。这一行使错误再次出现,表明问题与DB无关。。。当我这样做时,我看到
MvcHandler
对象被定义为一个类属性,这可能是并发性问题的根源(只有在执行了多个几乎连续的访问时,问题才会出现)。将
MvcHandler
对象移动到方法内部的本地对象

public class MyNewRouteHandler : IRouteHandler {

    public IHttpHandler GetHttpHandler(RequestContext requestContext) {
        IHttpHandler MvcHandler = new MvcHandler(requestContext);

        // some checkings and
        // some database access code 
        // that was commented

        return MvcHandler;
    }
}

经过测试,没有更多的错误。取消了我访问数据库的所有代码的注释(并进行了其他检查),仍然没有发现更多的错误。差不多三天过去了,一切都正常工作。

这种执行
自定义路由处理程序的方法确实解决了我的大部分错误,但我仍有一些错误和新消息。其中一个指向我的
自定义路由处理程序中的一个代码行,它们都有一个共同的事实,即MVC框架正在处理一个字典,所以。。。我仍然有并发问题吗


我假设是这样的,我的所有方法属性都被移动到
公共IHttpHandler GetHttpHandler(RequestContext RequestContext)
方法中,而不仅仅是前面提到的方法。其中一个是
RouteData
集合。。。最后,两天后,似乎不再显示错误。

您是否将可能被多个线程访问的任何对象作为模型传递?像一个共享的对象?vtorola不是真的!Wel所有模型都是从具有静态db上下文对象的BaseModel扩展而来的,但是它存储在HTTPContext中(使用一个键)。这样做是为了在“每个请求”的基础上使用它(在第一次访问时延迟加载)。堆栈跟踪不指向此代码,它们仅在视图逻辑中。此外,我之前已经在代码中添加了一个“lock”语句,用于初始设置DB上下文,以防万一。这似乎没有改变任何事情。我们使用的是Entity Framework.Woa,听起来很恐怖:D您应该使用DbContext作为控制器的一部分,从DB生成所需的POCO对象,然后创建视图模型。视图应该是线程安全的,我从未见过这样的错误,所以我建议开始深入研究这个令人毛骨悚然的共享DbContext。可能堆栈跟踪没有指向BaseModel对象,但它的一部分肯定是跨多个线程共享的。通常使用“基于每个请求”的数据库。我遵循“工作单元”模式。请参阅,更改此选项需要将所有静态DB访问函数更改为动态DB访问函数。这种结构在该应用程序的ASP.NET webforms版本上运行了多年,甚至直到今天,因为这个网站上仍然有数千名用户。(如果我们不解决这个问题,他们就不会切换到新网站:)。哦,是的,没关系。我的意思是,模型应该是非常简单的结构,表示视图的状态,没有复杂的层次结构能够创建它们的对象