【发布时间】:2012-12-10 10:06:26
【问题描述】:
我们最近开发了一个新的 ASP.NET MVC 4 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() 调用。这导致堆栈跟踪中没有更多视图,尽管一些 RenderPartial 现在也给出了一些
搜索此错误表明并发/并行执行是原因。这符合我们最初无法在本地重现错误的事实,但它确实发生在生产中。我们没有进行负载测试,但现在已经能够通过同时启动大量应用程序/请求在本地开发人员系统上重现错误。然而,在我们的代码中,没有任何事情是通过显式并行指令完成的。
这似乎与 MVC 视图不是线程安全的有关。然而,很难想象没有其他人会遇到这种情况。我们每天有几千名访客,任何时候大约有 30 多个活跃用户。遗憾的是,由于 Google 排名下降(与此问题相关),这个数字现在正在下降。
有人知道这个问题的解决方案/方法吗?
【问题讨论】:
-
您是否将任何可能被多个线程访问的对象作为模型传递?像共享对象?
-
vtorola 不是真的!好吧,所有模型都从具有静态数据库上下文对象的 BaseModel 扩展,但是它存储在 HTTPContext 中(使用键)。这样做是为了在“每个请求”的基础上使用它(它在第一次访问时被延迟加载)。堆栈跟踪不指向此代码,它们仅在视图逻辑中。此外,我之前已经在最初设置数据库上下文的代码上添加了一个“锁定”语句,以防万一。这似乎没有改变任何东西。我们正在使用实体框架。
-
哇,听起来令人毛骨悚然:D 您应该使用 DbContext 作为控制器的一部分,从数据库生成所需的 POCO 对象,然后创建视图模型。视图应该是线程安全的,我从未见过这样的错误,所以我建议开始深入研究那个令人毛骨悚然的共享 DbContext。也许堆栈跟踪没有指向那个 BaseModel 对象,但它的一部分肯定是在多个线程之间共享的。
-
嗯,经常使用基于“每个请求”的数据库。我遵循“工作单元”模式。请参阅blog.stevensanderson.com/2007/11/29/… 更改它需要我将所有静态数据库访问功能更改为动态功能。这个结构在这个应用程序的 ASP.NET webforms 版本上运行了很多年,直到今天,因为我们仍然有成千上万的用户在这个站点上。 (如果我们不解决这个问题,他们将不会切换到新网站:)。
-
哦,是的,没关系。我的意思是模型应该是代表视图状态的非常简单的结构,没有复杂的层次结构能够创建自己的数据库连接。这只是一个让细节更清晰的建议。为了让每个请求都有一个 DbContext,我使用了 IoC/DI 框架。
标签: concurrency asp.net-mvc-4 thread-safety renderaction