稀疏平常的ASP.NET Session Lost问题

发表于:2008-04-08来源:作者:点击数: 标签:
《Windows 用户态程序高效排错》市场价:35元 特价:29.8元 购买 1.3 简单的问题最棘手:稀疏平常的ASP.NET Session Lost问题 1.3.1 问题描述 客户抱怨,刚刚 开发 完成的大型ASP.NET站点 测试 阶段一切正常,但是放到生产环境上,运行压力一大,就会发生Ses

《Windows 用户态程序高效排错》市场价:35元 特价:29.8元 购买>>   

         1.3  简单的问题最棘手:稀疏平常的ASP.NET Session Lost问题

        1.3.1  问题描述

       客户抱怨,刚刚开发完成的大型ASP.NET站点测试阶段一切正常,但是放到生产环境上,运行压力一大,就会发生Session Lost现象。问题的表现是一个NullReferenceException异常。分析代码后发现,该NullReferenceException是试图访问一个SessionObject时候发生的。该SessionObject应该在前面就已经设置过。问题半年来一共就发生过3次,而且是在3个不同的页面发生。

        1.3.2  制定策略

       这个问题困难的地方在于重现的几率很小,没有多少详细观察的机会。所以,必须制订非常周全的计划,以便问题再次发生的时候,获取足够多的信息。如何制订周密的计划呢?

       思路非常直观,了解Session实现的细节,总结出导致问题的所有可能性,获取信息的时候排查所有的可能性。

       关于ASP.NET Session的细节,可以参考:

Underpinnings of the Session State Implementation in ASP.NET

有了对Session的理解后,把这个问题分成了下面几种情况:

        1.客户使用了负载均衡的环境,但是没有正确配置基于数据库或者服务的Session模式。或者是几台服务器的Machine key没有配置成一致。跟客户确认后排除了这种情况,因为客户只有一个服务器,使用的In-Proc。

        2. 最简单的情况就是所有的用户,所有的session都丢了。这种情况一般发生在In-Proc的session mode上。原因就是appdomain重启或者IIS进程崩溃。可以通过性能日志或系统日志来排查。

        3. 稍微麻烦一点的就是某一个用户的session丢了,而没有影响到所有用户。观察方法是,首先在session start里面做log,把每一个session的创建时间以及session id都记录到本地的一个log里面,同时往session里面添加一个测试用的session value。问题发生的时候,在ASP.NET的全局错误处理函数中当前的session id读出来,比较log中的记录看看这个session是不是刚刚建立的。如果是,很有可能是客户端的原因导致session id丢了,比如IE crash导致cookie丢失。

        4. 如果不是,那就看看测试用的session value是不是丢了。如果这个也丢了,应该是代码中掉了Session.Clear。

        5. 如果测试用的session value没有丢,情况就变成一个用户的session里面的一部分value丢了。很可能是由于用户的代码逻辑导致的。解决方法就是通过更详细的log来定位问题,然后阅读代码来检查。

可以看到,问题的特征跟潜在的根源是对应的。目的在于区分出这3种情况:

        1.所有用户的所有Session全没有了。

        2. 一个用户的Session没有了。

        3. 一个用户的部分Session没有了。

针对每种情况,采取的log策略是:

        对于第1类情况,可以在Application_Start/End函数中记录下时间来检查Appdomain是不是重新启动过。

        对于第2类情况,log文件应该记录下session id和session创建的时间。以便判断问题是否是cookie id lost导致的。如果是cookie id lost,那问题就出在客户端,或者是网络原因。

        对于第3类情况,可以在工程中搜索所有Session Clear的调用,每次调用前写log文件来记录。如果工程很大,无法逐一添加,可以加载调试器,在Session Clear函数中设定条件断点来记录。

         1.3.3  具体操作和结论

总结下来,具体的实现是:

        1.  在global.asax文件的session_start中把这个session的创建时间记录到session里面。这个创建时间也同时充当测试用的session value。

        2. 代码中对session操作的地方,写log到以sessionid为文件名的文件中去。

        3. 用log文件记录每次session的操作,发生在什么函数,发生的时间,session内容的变化。

        4.  当Exception发生的时候,在Exception handler中记录发生问题的session id和残留下来的Session value。

        这样,问题发生的时候,根据Exception handler记录的session id找到log文件,就可以很清楚地得到所需要的信息。

在做了上述部署之后,等了大约一个星期问题重现了。在log文件中,发现这样的信息:

        1. 某一个用户的部分Session丢失。

        2. 从Session创建时间看,该Session已经维持很长时间了。

        3. 通过检查Session Clear的调用纪录,发现丢失的Session的确是由用户自己的代码清除的。同时发现这些代码的运行次序跟设计不吻合。根据设计初衷,在清除 Session后,页面会重定向到一个专门的页面并重新添加Session,然后继续操作。但是log表明这个专门的页面并没有得到执行。

原文转自:http://www.ltesting.net