SqlConnection 立即超时

SqlConnection timing out instantly

编写一个非常简单的工具来 ping SQL 服务器并确保可以访问一堆连接字符串,我遇到了一个非常奇怪的问题:在迭代几十个连接字符串时,第一个在我指定的大约多少秒(在这种特殊情况下为 11 秒)并记录相应的尝试和异常当前墙时间后,有六个超时,但突然之间大约有十几个立即超时,然后恢复正常超时。

这是一台机器上的 运行,它无法访问所有要测试的连接字符串中使用的一个 IP。在日志中它看起来像这样:

2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:40     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:40     Connection name: 
2018-04-06 15:59:54     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:54     Connection name: 
2018-04-06 15:59:54     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 15:59:54     Connection name: 
2018-04-06 16:00:08     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 16:00:08     Connection name: 
2018-04-06 16:00:22     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 16:00:22     Connection name: 
2018-04-06 16:00:35     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s
2018-04-06 16:00:35     Connection name: 
2018-04-06 16:00:49     SQL Error:Connection Timeout Expired.  Th
or the server was unable to respond back in time.  The duration s

看看在一连串的即时超时后,在 15:59:40 记录的最后一个条目在 15:59:54 超时,这是正常的,然后另一个连接立即超时,16:00:08 之后的所有内容再次正常超时。

代码在我看来完全是天真的教科书:

foreach (ConnectionStringSettings cs in ConfigurationManager.ConnectionStrings)
{
    if (cs.Name.Equals("LocalSqlServer") && !args.Contains("NoSkipLocal", StringComparer.OrdinalIgnoreCase)) continue;

    SqlConnectionStringBuilder b = new SqlConnectionStringBuilder(cs.ConnectionString)
    {
        ConnectTimeout = timeout == 0 ? 10 : timeout
    };

    SqlConnection conn = new SqlConnection(b.ConnectionString);
    try
    {
        conn.Open();
        LogToConsole("Connection success", ConsoleColor.Green);
    }
    catch (SqlException sex)
    {
        LogToConsole("SQL Error:" + sex.Message, ConsoleColor.Red);
    }
    catch (Exception ex)
    {
        LogToConsole("Error:" + ex.Message, ConsoleColor.Red);
    }

static void LogToConsole(string message, ConsoleColor color = ConsoleColor.Gray)
{
    ConsoleColor fgColor = Console.ForegroundColor;
    Console.ForegroundColor = color;
    Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\t" + message);
    Console.ForegroundColor = fgColor;
}

其他人是否遇到过 ADO.NET SqlConnection 对象的即时超时?我原以为连接失败很可能是即时的,但至少消息应该有所不同。但它与过去 ~14 秒时完全相同。

Pooling = false 添加到连接字符串构建器实例化解决了这个问题。现在每个连接在抛出异常之前等待指定的秒数。

在连接池打开时处理 SqlConnection 是不够的,ADO.NET 池仍然持有特定数据库的条目并存储最后的错误状态,所以当下一个连接对象实例化时连接字符串指向相同,连接超时立即返回。

在我的特定情况下,这是一个很好的行为,因为它节省了我测试连接字符串的时间,但在现实世界中,它可能代表讨厌的副作用。