根据微软的文章(Server连接池(ADO.NET)),
当启用连接池时,如果出现超时错误或其他登录错误,将引发异常,随后的连接尝试将在接下来的5秒“阻塞期”失败。如果应用程序试图在阻塞期间内连接,则将再次引发第一个异常。阻塞周期结束后,应用程序的另一个连接故障将导致阻塞期,其长度是前一个阻塞周期的两倍。阻塞周期结束后的后续故障将导致一个新的阻塞周期,其长度是前一个阻塞周期的两倍,最长可达5分钟。
如何检测阻塞期是活动的?我假设在尝试连接之前有一些属性需要检查,这样就可以避免延长阻塞期。
发布于 2013-12-26 17:45:27
不幸的是,没有一种简单的方法来检测您是否处于ADO.NET“阻塞期”(而不是求助于一些脆弱的东西,比如反射)。
但是,如果您使用的是.Net 4.5或更高版本,那么您可以通过查看SqlException
的ClientConnectionId
并将其与您看到的最后一个SqlException
的id进行比较(因为异常是重复的,id也是重复的),就可以检测您在SqlException
/OpenAsync
中观察到的最后一个异常是否重复。
假设您有一个为单个连接字符串创建\open SqlConnections
的位置,则可以执行以下操作:
public static class DataAccessLayer
{
// Single connection string that all connections use
private static readonly string _connectionString = "server=(local);integrated security=true;";
// Stores that last observed connection if when opening a connection
// NOTE: Using an object so that the Volatile methods will work
private static object _lastErrorConnectionId = Guid.Empty;
public static SqlConnection GetOpenedConnection()
{
try
{
SqlConnection connection = new SqlConnection(_connectionString);
connection.Open();
return connection;
}
catch (SqlException ex)
{
// Did the connection open get to the point of creating an internal connection?
if (ex.ClientConnectionId != Guid.Empty)
{
// Verify that the connection id is something new
var lastId = (Guid)Volatile.Read(ref _lastErrorConnectionId);
if (ex.ClientConnectionId != lastId)
{
// New error, save id and fall-through to re-throw
// NOTE: There is a small timing window here where multiple threads could end up switching this between
// a duplicated id and a new id. Since this is unlikely and will only cause a few additional exceptions to be
// thrown\logged, there isn't a large need for a lock here.
Volatile.Write(ref _lastErrorConnectionId, (object)ex.ClientConnectionId);
}
else
{
// Duplicate error
throw new DuplicatedConnectionOpenException(_connectionString, ex);
}
}
// If we are here, then this is a new exception
throw;
}
}
}
public class DuplicatedConnectionOpenException : Exception
{
public string ConnectionString { get; private set; }
internal DuplicatedConnectionOpenException(string connectionString, SqlException innerException)
: base("Hit the connection pool block-out period and a duplicated SqlException was thrown", innerException)
{
ConnectionString = connectionString;
}
}
现在,如果您调用GetOpenedConnection
并看到抛出一个DuplicatedConnectionOpenException
,您就会知道您已经进入了“阻塞期”。
注意:我在这里使用的是Volatile
Read
/Write
,而不是锁,因为我选择更好的性能,而不是100%准确地表示处于“阻塞期”。如果您想要的准确性,您可以使用lock
代替。
此外,我确实有代码作为SqlConnection
上的扩展方法,可以处理多个连接字符串,但是它的性能要差得多,因为它使用ConcurrentDictionary
将连接字符串映射到连接it。
发布于 2013-11-23 18:16:57
不应该需要检查是否处于阻塞期以避免扩展。正如上面的摘录所述,任何在阻塞期间连接的尝试都将重新抛出第一个异常,它没有提到延长阻塞期。然而,每一个新的阻塞期将是以前的两倍。
在我的经验中,抛出的异常(由于超时、连接泄漏等)是否存在环境问题或未能正确关闭/处理连接。记录这些异常是个好主意,这样您就可以追踪真正的问题了。
如果您确实经常遇到超时异常,您可以捕获它并尝试清理所有的游泳池,但很可能是由于连接泄漏。您需要确保用using语句包装连接,这将有助于在完成连接或发生异常时关闭/处理连接。
using(SqlConnection connection = new SqlConnection("connection_string"))
{
using(SqlCommand command = new SqlCommand())
{
SqlCommand command = new SqlCommand();
command.Connection = connection;
command.CommandType = CommandType.Text;
command.CommandTimeout = [some timeout value];
command.CommandText = "Update SomeTable Set Value = 1";
connection.Open();
command.ExecuteNonQuery();
}
}
发布于 2017-11-18 17:31:32
除了ClientConnectionId字段之外,
SqlException.Message也将是参考-平等。也就是说,对于在“阻塞期”内失败的连接,将返回缓存的字符串。
然而,这也是一个实现细节,可能会发生变化。
https://stackoverflow.com/questions/20151652
复制相似问题