Как сделать тайм-аут SqlConnection более быстрым
Я использую строку соединения SQL с SqlClient.SqlConnection и указывая время ожидания соединения = 5 в строке, но она все еще ждет 30 секунд перед возвратом отказа. Как мне заставить его сдаться и вернуться быстрее? Я нахожусь в быстрой локальной сети и не хочу ждать 30 секунд. Серверы, которые не включены, задерживают 30 секунд. Это просто программа для быстрой утилиты, которая всегда будет работать только в этой локальной сети.
Изменить: Извините, если я не понял. Я хочу, чтобы SqlConnection.Open терпел неудачу быстрее. Надеюсь, это можно было бы вывести из того факта, что серверы, которые я хочу сбой быстрее, отключены.
Изменить. Кажется, что настройка иногда не срабатывает. Как он знает IP-адрес сервера и использует TCP/IP для разговора (не локальный), но не может связаться с SQL Server по этому адресу? Я не уверен, что такое шаблон, но я не вижу проблемы при локальном подключении с SQL Server, и я не вижу его при попытке подключиться к несуществующему серверу. Я видел это при попытке связаться с сервером, где брандмауэр Windows 2008 блокирует SQL Server.
Ответы
Ответ 1
Похоже, что все случаи, которые вызывают длительные задержки, могут быть разрешены гораздо быстрее, пытаясь установить прямое соединение со следующим:
foreach (string svrName in args)
{
try
{
System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient(svrName, 1433);
if (tcp.Connected)
Console.WriteLine("Opened connection to {0}", svrName);
else
Console.WriteLine("{0} not connected", svrName);
tcp.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error connecting to {0}: {1}", svrName, ex.Message);
}
}
Я собираюсь использовать этот код, чтобы проверить, отвечает ли сервер на порт SQL Server, и только пытайтесь открыть соединение, если это произойдет. Я подумал (по опыту других), что на этом уровне будет 30-секундная задержка, но я получаю сообщение о том, что машина "активно отказалась от соединения" сразу после этого.
Изменить: И если машина не существует, она сразу же сообщает мне об этом. Нет 30-секундных задержек, которые я могу найти.
Изменить: Машины, которые были в сети, но не отключены, до сих пор занимают 30 секунд. Однако брандмауэр-машины работают быстрее.
Изменить: Здесь обновленный код. Я чувствую, что это чище, чтобы закрыть сокет, чем прервать поток:
static void TestConn(string server)
{
try
{
using (System.Net.Sockets.TcpClient tcpSocket = new System.Net.Sockets.TcpClient())
{
IAsyncResult async = tcpSocket.BeginConnect(server, 1433, ConnectCallback, null);
DateTime startTime = DateTime.Now;
do
{
System.Threading.Thread.Sleep(500);
if (async.IsCompleted) break;
} while (DateTime.Now.Subtract(startTime).TotalSeconds < 5);
if (async.IsCompleted)
{
tcpSocket.EndConnect(async);
Console.WriteLine("Connection succeeded");
}
tcpSocket.Close();
if (!async.IsCompleted)
{
Console.WriteLine("Server did not respond");
return;
}
}
}
catch(System.Net.Sockets.SocketException ex)
{
Console.WriteLine(ex.Message);
}
}
Ответ 2
Обновление 2
Я предлагаю перевести свой тайм-аут. Что-то вроде этого:
internal static class Program
{
private static void Main(string[] args)
{
Console.WriteLine(SqlServerIsRunning("Server=foobar; Database=tempdb; Integrated Security=true", 5));
Console.WriteLine(SqlServerIsRunning("Server=localhost; Database=tempdb; Integrated Security=true", 5));
}
private static bool SqlServerIsRunning(string baseConnectionString, int timeoutInSeconds)
{
bool result;
using (SqlConnection sqlConnection = new SqlConnection(baseConnectionString + ";Connection Timeout=" + timeoutInSeconds))
{
Thread thread = new Thread(TryOpen);
ManualResetEvent manualResetEvent = new ManualResetEvent(false);
thread.Start(new Tuple<SqlConnection, ManualResetEvent>(sqlConnection, manualResetEvent));
result = manualResetEvent.WaitOne(timeoutInSeconds*1000);
if (!result)
{
thread.Abort();
}
sqlConnection.Close();
}
return result;
}
private static void TryOpen(object input)
{
Tuple<SqlConnection, ManualResetEvent> parameters = (Tuple<SqlConnection, ManualResetEvent>)input;
try
{
parameters.Item1.Open();
parameters.Item1.Close();
parameters.Item2.Set();
}
catch
{
// Eat any exception, we're not interested in it
}
}
}
Обновление 1
Я только что протестировал это на своем собственном компьютере, используя этот код:
internal static class Program
{
private static void Main(string[] args)
{
SqlConnection con = new SqlConnection("Server=localhost; Database=tempdb; Integrated Security=true;Connection Timeout=5");
Console.WriteLine("Attempting to open connection with {0} second timeout, starting at {1}.", con.ConnectionTimeout, DateTime.Now.ToLongTimeString());
try
{
con.Open();
Console.WriteLine("Successfully opened connection at {0}.", DateTime.Now.ToLongTimeString());
}
catch (SqlException)
{
Console.WriteLine("SqlException raised at {0}.", DateTime.Now.ToLongTimeString());
}
}
}
и он соответствует значению Connection Timeout
в строке подключения. Это было с .NET 4 против SQL Server 2008 R2. По общему признанию, это соединение с localhost, которое может давать разные результаты, но это означает, что я не могу реплицировать проблему.
Я могу только предположить, пытаясь аналогичный фрагмент кода в сетевой среде и, видя, если вы будете продолжать видеть длинные перерывы.
Старый (неверный) ответ
Я неправильно думал, что свойство ConnectionTimeout
можно установить, но это не так.
Попробуйте установить SqlConnection.ConnectionTimeout вместо использования строки подключения.
Ответ 3
Тайм-аут команды и таймаут соединения - это две разные вещи.
SqlConnection.ConnectionTimeout - это время (в секундах) ожидания открытия соединения. Значение по умолчанию - 15 секунд." Это используется только при вызове SqlConnection.Open().
SqlCommand.CommandTimeout делает то, что вы хотите сделать.