连接池中溺死重生,多亏NET 连接池救生员

Posted on 2006-09-07 15:36  苹果引擎  阅读(951)  评论(1编辑  收藏  举报
您的应用程序会“沉没”,还是会“游泳”?“NET 连接池救生员”,msdn上这篇有名的文章n久之前已经拜读过n次,当时还暗自庆幸,我的程序没有发生过这样的情况。就在系统代码设计即将结束,客户培训接近尾声,部分科室已经运行的时候,令所有的使用者和程序员崩溃的事情发生了,程序出现连接已满的错误,而且是发生的很快。
    这时候想起来这篇文章,经过排查,问题终于告一段落。
   情况是这样:
   程序的设计人员都是第一次用.net开发系统,对他的理解也深浅不一,所以写出的代码也
各种各样。设计的功能基本达到了要求,而且已经在现场开始使用,当时的操作系统win2k,sqlserver数据库,运行也算正常。就在认为可以系统基本稳定了,然后服务器升级换代,大部分科室开始使用,操作系统改为win2003,出现了异常情况。连接已满,空引用的错误。

   分析得出,可能的问题有几个:
系统代码更新错误,操作系统升级设置不对,科室使用人员
原先没有使用到的模块,现在暴露出代码问题。
首先将代码还原成更新以前,结果还是同样的异常。
然后服务器问题,设置iis连接池,回收时间,web圆,简直是有病乱投医,没有作用,依然如故。
然后分析系统的近期开始使用的代码,果然,有许多没有释放的连接,其中这一块代码是一个程序员写的,他当时不在。
 
   经过排查,发现问题如下:
打开连接
sql语句赋值
SqlCommand cmd = new SqlCommand(cnString,cn);
SqlDataReader dr=cmd.ExecuteReader(CommandBehavior.CloseConnection);
..
未发现关闭连接或者DataReader。
  但是按照微软在MSDN上的描述:CommandBehavior.CloseConnection参数的作用是:
When   the   command   is   executed,   the   associated   Connection  
object   is   closed   when   the   associated   DataReader   object   is  
closed.
也就是说CommandBehavior.CloseConnection参数的作用是在关闭DataReader之后,自动关
闭打开的数据库连接。
而William Vaughn却说:
在整个 DataReader 结果集中循环到其行集的末尾(也就是说,当 Dr.Read — DataReader 的 Read 方法 — 返回 False 时)还不足以触发连接的自动关闭。不过,如果您绑定到一个复杂的绑定控件(例如,DataGrid),该控件则会关闭 DataReader 和连接 — 前提条件是您设置了 CommandBehavior.CloseConnection 选项。
可以看到,如果不关闭连接或者DataReader是绝对不行的,
关闭DataReader,如果选择了CommandBehavior.CloseConnection,即使关闭DataReader,
连接也不会很快关闭。
如果是作为DataGrid的数据源,如果选择了CommandBehavior.CloseConnection,DataGrid
是可以自动关闭DataReader 和连接 。
然后经过一番修改,程序发布,然后信息十足的等待消息,结果没出10分钟,程序又一次出
现了困扰已久的问题,连接池满。晕倒!
在一次将眼光集中到,程序上面,看来那一块程序已经没有多大问题了,看看其他的程序。
别的地方,是另一个程序员写的,里面数据库访问,用的是data access  application
block,经过刚才的发现,我开始怀疑是不是这里用法有误,发现里面的许多地方是
SqlDataReader dr=SqlHerper.ExecuteReader(...);
但是都没有关闭,好像说明上说他可以自动关闭,还是自己看看吧,
于是在while(dr.reder()){}
之后,我判断dr.isClosed,返回的是false,说明不关闭也是不行的,打开他的源码,果然他用的就是那个CommandBehavior.CloseConnection,看来他也是寄希望于关闭DataReader
之后,自动关闭连接。如果连接都不关闭,他还不漏到底了。如果邦定到DataGrid上,就不用关闭了这是为什么呢??
在csdn上发现了答案。
http://blog.csdn.net/levin9/archive/2006/02/14/599115.aspx
人家分析的很清楚啊!
把DataReader作为DataGrid的DataSource时,DataReader是当作IEnumerable来看待的,从IEnumerable可以获取IEnumerator,然后可以用它的MoveNext/Current来依次获取每个记录,难道MoveNext调用了DataReader的Close方法,而由于设定了CommandBehavior.CloseConnection,连接也在DataReader关闭时关闭了。

终于找到了这支大臭虫,解恨阿!
 
于此同时,我也用了几个平时懒得理的工具:
application center test,微软自带的压力测试,挺爽,但是发现在xp系统上不能用,
可能是系统设置的问题,我还没找到。
然后都是人家http://www.microsoft.com/china/MSDN/library/data/sqlserver/TheNETConnectionPoolLifeguard.mspx说的,都很详细。那个控件监控也没有看。
连接池计数器在哪里
要监视连接池计数器,您必须监视 ADO.NET 在其中创建和增加这些计数器的系统。如果您从远程系统进行连接,ADO.NET 并不总是在 Microsoft IIS 服务器或 SQL Server 上创建池;它在 ADO.NET 代码运行的系统上创建池。此系统可以是运行 IIS、Web 应用程序或 Web 服务的远程 Windows 或中间层系统。相反,SQL Server 性能计数器位于 SQL Server 系统上 — 而不是客户端上。
使用性能监视器来监视池。
   如果您使用 Microsoft 管理控制台 (MMC) Windows 2000 系统监视器管理单元,则您可以通过从 Performance 对象下拉列表中选择 ".NET CLR Data" 来用图形表示 SqlClient 计数器,如 图 1所示。请注意,您可以通过选择 _global_ 计数器实例来监视所有进程,或者,您可以查看某个特定实例 — 每个池生成自己的一组监视器。性能监视器可列出这些计数器,并将它们作为所选定的性能对象的实例提供。但性能监视器不会公开这些计数器,除非有实例需要它们进行监视。例如,图 1 显示了 .NET CLR Data 性能对象,但没有列出特定实例。这意味着您必须至少创建一个连接,以便使 _global_ 实例连同每个进程的特定实例一起出现。这种行为对于您的代码来说是个问题;您将无法使用 PerformanceCounter 控件来返回其中的任何计数器,直到 ADO.NET 在打开连接时创建这些计数器。所以说,这个规定真有点令人左右为难。当您使用此方法时,因为缺少有效计数器实例,所以会引发异常 — 此时要准备好捕获异常。
 
   总之,这些错误看起来有些弱,但是他说明的问题却很多,如果前期设计的好,数据访问设计完善,就是出问题,也很快可以发现了。如果多作测试也不会集中爆发。太多如果了,如果Boss能按照软件工程让你做程序,那就好了,可惜啊!

Copyright © 2024 苹果引擎
Powered by .NET 9.0 on Kubernetes