《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表明这个专门的页面并没有得到执行。