子非鱼,安知鱼之乐?

我的程序人生,累并快乐着。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

NHibernate在Asp.net中的实际应用

Posted on 2006-09-13 10:12  Jason Cui  阅读(3623)  评论(13编辑  收藏  举报

半年前开始研究NHibernate,受http://www.codeproject.com/aspnet/NHibernateBestPractices.asp这篇文章的导向,开始按照它的方式来使用NHibernate,作者还称之为最佳实践。看上去的确如此,充分的考虑到了效率问题,把SessionFactory放到Application级的缓存中,把Session放到Context的缓存中,每个Request开始的时候Open一个Session,结果的时候Close它。而且在它的源码中,Flush也是在Close的时候进行的。
这样做的一开始就遇到一个问题,就是会出现一些奇怪的保存数据的情况。测试人员比较少,没有及时发现问题,等到网站发布出去才发觉,很多用户的密码被清空了。变成了Null。赶紧想原因,原来用了一个精简版的User类保存在Session中,不必每次都去数据库里取他的信息,而在用户未登录之前,这个类是New出来的。这样,在请求结束的时候,这个User就被保存回去了,导致出现了很多空的匿名用户,还有很多用户的密码被清空了。
好不容易解决了这个问题,取消了在Request结束的时候保存数据,而是每个动作后直接Flush。此后一直相安无事。直到这两天升级成Asp.net 2.0,每到下午2点左右的时候就会出现严重的异常导致系统的w3wp进程不停的重启,系统无法正常工作,而切换回1.1版本又没有问题。为了这个问题搞的我一周时间焦头烂额。直到昨天才找到问题的根源。这个异常是由数据库连接池引起的。日志信息里显示检测到死锁,一开始一直不知道这个死锁指的是什么。现在才明白,是数据库连接的死锁。
在1.1的状态下,如果数据库连接被用满,下一个用户会看到服务器错误的页面,等待前面的连接释放掉,后面的用户才能再访问。这时候出现的异常信息被1.1自己处理掉了。而到了2.0,这个异常属于进程外的异常信息,会被抛出,导致进程的死掉。
由于Session被放到Context的上下文里,每个连接建立一次Session。当上文的作者假设你的每个请求处理速度都快到可以忽略的时候,这是没有问题。然而在我们的网站里,每个页面里可能有数十次数据库处理,整个页面的生成时间超过1秒,这就变成了整个网站所能承受的并发会话数直接受到了数据库连接池所允许的连接数的限制。于是再次修改程序,把Session变成每次使用数据库之前Open,读取完成马上Close。按照原作者的分析,与数据库之间的Connection的Open和Close是相当费时的操作,然而在.Net平台下,所有的连接默认受到数据库连接池的处理,这个过程其实已经被缓存过了,没必须再占着Connection不放了。
另外,为了避免系统两次出现不可知的异常导致服务器当机,直接修改了aspnet.config文件,把未知异常的处理方式修改回1.1默认的方式,也就是忽略它,继续执行。虽然微软不推荐这样做,但是在这样一个没有足够的测试时间和能力的环境下,也只好这样了。