SQL Server断开连接导致连接池问题

3

我有一个运行在Windows上的服务,在通过RabbitMQ接收到消息后,触发事件处理程序进行一些操作,然后尝试将结果保存到数据库中。它使用线程:

ThreadPool.QueueUserWorkItem(ProcessMessageOnThread, messageReceived);

ProcessMessageOnThread 是一个方法,用于处理从 RabbitMQ 队列中出列的消息的表示形式 messageReceived

在正常情况下,Windows 服务按预期运行,即出列、处理和持久化。

我希望确保所有的消息都被处理并有公平机会得到处理,因此如果无法连接到 SQL Server,我只需将消息重新排队以便再次处理它(希望那时 SQL Server 可以连上,否则该过程会继续进行 - 我可以接受这种情况)。

现在问题来了,当进程按预期运行一段时间后,SQL Server 连接池已经填满,并且然后 SQL Server 断开连接,这就是事情变得有点不稳定的时候。

可能会发生以下两种情况之一:

  • connection.Open() 抛出异常 - 不过我正在捕获异常,所以不用担心

  • cmd.ExecuteNonQuery() 抛出异常 - 这是我执行存储过程的地方

第二种情况是我需要解决的。此前我认为任何在此处抛出的异常意味着我传递给存储过程的数据有问题,因此应将其从队列中移出并由其他东西进行分析。

但是,现在我认为我需要一种新的方法来处理异常,以处理与连接未实际建立有关的异常情况。

我查看了 SqlException 类,并注意到一个名为 Class 的属性,其描述为 获取从 SQL Server 返回的错误的严重级别,现在对此的信息说:

严重级别小于等于 10 的消息是信息性的,指示由用户输入错误引起的问题。级别从 11 到 16 是用户生成的,可以由用户进行更正。级别从 17 到 25 表示软件或硬件错误。发生级别 17、18 或 19 错误时,您可以继续工作,尽管可能无法执行特定语句。

这是否意味着为解决我的异常处理问题,我只需检查 if (ex.Class > 16),然后重新排队消息,因为问题是连接,否则,将其丢弃,因为它很可能与发送到存储过程的格式不正确的数据有关?

所以问题是,我应该如何进行异常处理,以及如何检测调用 cmd.ExecuteNonQuery() 时抛出的异常是否由于断开连接而引起。


更新:

我之前遇到过连接没有返回池的问题(这是由于线程问题引起的),并已经解决了这些问题,所以我相信问题与连接未返回池无关。此外,连接使用的逻辑也很简单,我确保它们被一致地关闭...所以我更关心的是与 SQL Server 断开连接以及捕获 cmd.ExecuteNonQuery() 行为的答案。

1个回答

3
连接池中的连接可能因各种原因而进入奇怪的状态,所有这些原因都与应用程序设计不良有关:
  • 在相关数据读取器关闭之前关闭连接
  • 更改池不重置的设置(如事务隔离级别)
  • 开始异步查询(BeginOpenReader),然后在异步处理程序触发之前将连接返回到池中

您应该调查应用程序并确保连接正确地返回到池中。在开发环境中减小应用程序池的大小可以帮助调试。您可以在连接字符串中更改池的大小。

...;Integrated Security=SSPI;Max Pool Size=2;Pooling=True;

这使得池化问题更容易重现。
如果您无法找到原因,但仍需要部署修复程序,则可以使用其中之一ClearPoolClearAllPools。一个好的时机是在检测到Open()ExecuteNonQuery()后出现可疑异常时。两者都是SqlConnection类的静态方法:
SqlConnection.ClearPool(yourConnection);

或者采用更加粗略的方法:

SqlConnection.ClearAllPools()

注意,这基本上是宝可梦异常处理。如果它可以工作,你就不会知道为什么。 :)

我曾经遇到过连接没有返回到池中的问题(这是由于线程问题引起的),并且已经解决了这些问题,因此我相信问题与连接没有返回到池中无关。此外,围绕连接使用的逻辑非常简单,我正在确保它们始终关闭...因此我更感兴趣的是与 Sql Server 断开连接,然后捕获 cmd.ExecuteNonQuery() 行为的答案。 - kzhen

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