ASP.NET随机丢失会话值

9

我已经花了相当长的时间寻找答案,因为这个问题一直困扰着我。我们将用户登录信息和其他有关用户当前活动的数据存储在会话状态(InProc)中。偶尔我会得到一个空引用异常,尝试使用其中一个会话变量。它发生在随机页面上,随机会话变量上。我已经修改了web.config的httpRuntime和compilation标记以防止appPool重新启动:

<httpRuntime requestValidationMode="2.0" waitChangeNotification="86400" maxWaitChangeNotification="86400" />
<compilation debug="False" strict="false" explicit="true" targetFramework="4.0" numRecompilesBeforeAppRestart="1000" />

我已经设置了IIS在凌晨3点重新启动应用程序池,以确保它不会在人们繁忙使用服务器时重新启动。同时,我将应用程序池重启记录在事件日志中,以确保我知道何时发生。

Dim runtime As HttpRuntime = GetType(System.Web.HttpRuntime).InvokeMember("_theRuntime", BindingFlags.NonPublic Or BindingFlags.Static Or BindingFlags.GetField, Nothing, Nothing, Nothing)
Dim shutDownMessage As String = runtime.GetType().InvokeMember("_shutDownMessage", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.GetField, Nothing, runtime, Nothing)

Dim shutDownStack As String = runtime.GetType().InvokeMember("_shutDownStack", BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.GetField, Nothing, runtime, Nothing)
Dim evtSource As String = "ASP.NET"
Dim log As New EventLog
log.Source = evtSource
log.WriteEntry(String.Format("_shutDownMessage={0}{2}_shutDownStack={1}", shutDownMessage, shutDownStack, vbCrLf & vbCrLf), EventLogEntryType.Warning)

当应用程序池重启时,我会收到事件日志条目。但是这些错误发生时,应用程序池并没有重新启动。

当特定的会话变量丢失时,同一用户的大多数其他会话变量仍然存在。此外,通常有另外10-20个登陆该站点的用户不受影响。
出现错误的用户将返回,再次浏览相同的页面,然后就可以正常工作了。

我曾在运行IIS6且安装了.NET 3.5 32位和4GB内存的Windows Server 2003(32位)上遇到过这个问题。一年前我们进行了服务器升级,得到了一个新的Web服务器 - 运行IIS 7的Windows Server 2008(64位),内存为16GB。我将网站升级到了.NET 4.0 64位。在新机器上仍然遇到同样的问题(每天通常发生1-3次 - 在一天中的任意时间)。

由于它的随机性,我无法在调试中重现这个问题,但我确实认为它也会随机出现在我们的开发环境中。开发服务器的规格与生产服务器几乎相同。
两个环境都是隔离的,并作为单个Web服务器运行,而不是Web farm的一部分。

我想我可以尝试实现一个状态服务器以退出InProc模式,但这只是在瞎猜。
除了尝试状态服务器之外,还有什么其他方法可以识别此时的情况或防止它发生吗?


尝试在StackOverflow上搜索,有很多相同的问题和许多建议。 - sll
你是否在负载均衡器后运行两个Web服务器?(并且你确定已经仔细检查了代码中设置的位置,以确保它们没有被覆盖吗?) - Paddy
我已经反复查看了代码,寻找我可能无意中覆盖它们的地方,但没有找到任何我正在覆盖的地方。此外,它发生在许多不同的页面上,会话变量也不同 - 并且使用不同的用户/浏览器/平台。我找不到这种情况发生时的任何关联。 - Stephen
1
值得再次确认一下,您是否有任何空的catch块。如果您尝试向会话中添加一个不可序列化的对象(或包含一个不可序列化的嵌套对象),它将抛出异常。但是,如果您捕获并忽略了该异常,您可能会遇到这样一种情况:您认为已经向会话中添加了某些内容,但实际上却悄悄地失败了。 - mikemanne
@mikemanne - 这是一个好想法,如果/当我实现状态服务器时,我一定会检查NonSerializable对象,但由于它目前是InProc,我可以将任何对象添加到Session中,而不仅仅是Serializable对象。这是我还没有使用状态服务器的原因之一 - 需要检查所有对象是否可序列化,需要花费很多时间。 - Stephen
6个回答

7
如果您的 Web 应用程序部署在服务器农场上(超过一个服务器 Web),就像您所说,您正在使用 InProc 会话,可能会发生用户被重定向到已存储该会话变量的不同服务器的情况。在这种情况下,您应该选择一个 out of proc 会话,正如您所提到的(Session State Server)
如果您选择 State Server,请记住以下内容以防止任何其他问题:
由于 Stateserver 将 ASP.NET 会话 ID 与 IIS 应用程序路径结合起来创建唯一键,因此为五个新网站之一发出的会话在通过其中一个其他网站访问时可能无法找到,这显然在加权轮询负载平衡 Web 农场中非常不幸。

http://www-jo.se/f.pfleger/session-lost

同时也可以查看这个日志记录器,以了解应用程序是否反对您的意愿进行了重启:

http://weblogs.asp.net/scottgu/archive/2005/12/14/433194.aspx

http://blogs.msdn.com/b/tess/archive/2006/08/02/asp-net-case-study-lost-session-variables-and-appdomain-recycles.aspx


这也是我的第一个想法。然而,由于他说“当特定的会话变量丢失时,同一用户的大多数其他会话变量仍然存在”,我不太可能怀疑会话亲和性失败。 - mikemanne
1
这可能发生。也许用户在服务器1上(假设创建了一些会话变量),然后如果请求由服务器2提供并且代码尝试访问该服务器上不存在的会话变量,则会得到空引用。 - Massimiliano Peluso
对不起,我应该也说明一下。只有一个服务器,不是服务器群。 - Stephen
我以前看过ScottGu的页面。我使用了他的一些代码来处理应用程序池关闭日志记录 - 我已经更新了原始帖子并添加了那段代码。问题是,我认为根本没有发生这种情况...其他所有在线用户都保持活动状态,并且不会失去任何会话值。此外,发生这种情况的用户仍然处于活动状态(其用户信息在会话中),并且他们的其他会话值也不受影响。 - Stephen
尝试使用最后链接中显示的“Health Monitoring Events”来确定导致应用程序域重启的原因。 - Massimiliano Peluso
@Massimiliano - 你提到旧会话可能仍然存在于不同的负载均衡服务器上,这是一个很好的观点。听起来对他来说已经无关紧要了(只有1个服务器)。但是我还是没有想到这一点! :) - mikemanne

7
对于任何对此感兴趣或遇到类似问题的人,我想跟进一下我的问题的原因。大约7或8个月前,我为应用程序缓存和会话状态实现了一个NCache外部进程状态服务器。不幸的是,将会话移出进程并没有对我在网站上选择报告时丢失随机会话变量的问题产生任何影响。由于我一直无法复制此问题,所以一直没有更多努力来尝试修复它,直到最近另一个问题让我恍然大悟。
重点是:我没有在意识到的某个地方覆盖会话变量,而是用户打开第二(或第三)个选项卡来并排比较报告选择。我们有几个自定义报告,用户可以选择多个选项生成自定义报告(就像向导控件一样,需要几个步骤才能创建自定义报告)。如果用户处于5个步骤中的第3个步骤,然后打开一个新选项卡并开始再次进行报告选择过程,则新选择将覆盖旧选择,因为两个选项卡共享同一个会话。通过打开多个选项卡并逐步完成选择过程,我验证了这一点。
我正在尝试区分多个报告运行,以便使用唯一的会话密钥存储一个报告的选择与其他报告选择。这也很困难,但与我认为的丢失会话数据的问题没有真正关系。
如果有人发现此帖子并认为自己随机丢失会话数据,并且无法复制它,请尝试调试您的网站并打开多个选项卡。同时逐步完成两个选项卡可以让我清楚地看到问题所在。
希望对你有帮助。

1
有一个类似的问题,问题很难复制...(其他用户没有这个问题,我也不认为浏览器会引起这个问题,因为浏览器的工作只是发送sessionid)。但上面是一个逻辑上的原因,我们将探讨用户是否打开了多个页面。 - visual
通过使用更好的设计来避免这个多页面问题。从会话变量中删除所有客户端状态,并将它们放在查询字符串上,例如?reportname=summary&page=1&pagelength=20&reportparameter1=etc。我的观点是,会话仅在用户登录或返回带有cookie时存储userid或哈希值时才有用。 - Awerealis
@Awerealis,是的,如果只有几个参数,通过查询字符串传递参数确实是一个选项。不幸的是,我的页面上有很多内容,而且我已经在使用ScriptManager历史状态,它会用哈希值加载URL: <asp:ScriptManager ID ="ScriptManager1" runat ="server" EnableHistory ="true" EnableSecureHistoryState ="false"> 由于历史状态参数数量庞大,我不得不增加httpRuntime maxURLLength,因为260长度不够。更不用说我们不想/不需要向客户端公开的会话中有很多东西了。 - Stephen

1
由于我花了一些时间才弄清楚这个问题,所以我想在这里发布它,以防对其他人有所帮助。
我遇到了一个情况,即IE和Chrome也会随机删除会话变量。 我搜索了很多并且每个人都说了通常的事情......检查域名,检查您的IIS设置以获取cookie等等。
我的问题最终是权限问题。
在我的web.config中,我有一个权限条目,用于可以被未经身份验证的公众访问的“public”文件夹。
<location path="public">
<system.web>
  <authorization>
    <allow users="*" />
    <allow users="?" />
  </authorization>
</system.web>

问题是公共端调用了一个不在公共端的HttpHandler .js。 为了重复使用代码,我将安全端和公共端都指向了安全端的代码。我猜作为副作用,它杀死了会话,没有非常有意义的错误信息。 我可以为该处理程序添加另一个条目,或者我可以制作该代码的公共和安全副本(这是一种不太理想的方法)。

1
我遇到了这个问题,因为我们的服务器设置了运行https。如果我使用简单的http运行,会导致会话无法保留。但是,当在https上运行时,会话得以保留。因此,我们设置了一个URL重写规则,如果用户通过http进入应用程序,则始终将其发送到https。
另外,请注意,除非您正在运行https(请注意https末尾的S),否则本地或服务器上的会话将无法工作,如果您在web.config文件中有以下内容:
<httpCookies httpOnlyCookies="true" requireSSL="true"/>

1

您遇到的会话问题可能是由多种原因引起的

  1. 会话过期:由于您正在使用Inproc模式,会话仅在sessiontimeout时间段内有效。默认情况下为20分钟。尝试在web.config的system.web部分中使用sessionstate标记,并将超时值设置为较大的值。

  2. 另一个问题可能是由于Web农场和Web Garden。如果您已经为您的网站配置了Web农场和Web Garden,则Inproc会话共享可能会导致问题。

  3. 进程重启:您网站的w3p进程由于代码中的某些问题或内存泄漏而被重新启动。


0

还有一种情况是会话可能会失去其值。

您可以使用 Fiddler 工具来跟踪此问题。

最常见的情况是在解决方案中找不到某些元素,例如源。此时,服务器将尝试通过重新启动项目来重新加载未找到或丢失的对象。重新启动项目将导致重置所有会话对象。

谢谢。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接