Ajustando HttpWebRequest Connection Timeout en C#
(5)
Algo que encontré más tarde que ayudó, es la propiedad .ReadWriteTimeout
. Esto, además de la propiedad .Timeout
, parecía finalmente reducir el tiempo que los hilos pasarían tratando de descargar desde un servidor problemático. El tiempo predeterminado para .ReadWriteTimeout
es de 5 minutos, lo que para mi aplicación fue demasiado largo.
Entonces, me parece a mí:
.Timeout
= tiempo dedicado a intentar establecer una conexión (sin incluir el tiempo de búsqueda) .ReadWriteTimeout
= tiempo dedicado a intentar leer o escribir datos una vez establecida la conexión
Más información: HttpWebRequest.ReadWriteTimeout Property
Editar:
Por el comentario de Per @ KyleM, la propiedad Timeout
es para todo el intento de conexión, y la lectura en MSDN muestra:
El tiempo de espera es el número de milisegundos que una solicitud síncrona posterior realizada con el método GetResponse espera una respuesta, y el método GetRequestStream espera una transmisión. El tiempo de espera se aplica a toda la solicitud y respuesta, no individualmente a las llamadas al método GetRequestStream y GetResponse. Si el recurso no se devuelve dentro del período de tiempo de espera, la solicitud arroja una WebException con la propiedad Estado establecida en WebExceptionStatus.Timeout.
(Énfasis mío)
Creo que después de una larga investigación y búsqueda, descubrí que lo que quiero hacer probablemente sea más útil configurando una conexión asincrónica y finalizándola después del tiempo de espera deseado ... ¡Pero seguiré y preguntaré de todos modos!
Fragmento de código rápido:
HttpWebRequest webReq = (HttpWebRequest)HttpWebRequest.Create(url);
webReq.Timeout = 5000;
HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();
// this takes ~20+ sec on servers that aren''t on the proper port, etc.
Tengo un método HttpWebRequest
que está en una aplicación multiproceso, en la que me estoy conectando a una gran cantidad de servidores web de la empresa. En los casos en que el servidor no responde, la HttpWebRequest.GetResponse()
tarda unos 20 segundos en HttpWebRequest.GetResponse()
, aunque he especificado un tiempo de espera de solo 5 segundos. Con el interés de pasar por los servidores en un intervalo regular, quiero omitir aquellos que tardan más de 5 segundos para conectarse.
Entonces, la pregunta es: "¿Existe una manera simple de especificar / disminuir el tiempo de espera de una conexión para una WebRequest o HttpWebRequest?"
De la documentación de la propiedad HttpWebRequest.Timeout:
Una consulta del Sistema de nombres de dominio (DNS) puede demorar hasta 15 segundos para regresar o expirar. Si su solicitud contiene un nombre de host que requiere resolución y establece Timeout en un valor de menos de 15 segundos, puede tomar 15 segundos o más antes de que se genere una WebException para indicar un tiempo de espera excedido en su solicitud.
¿Es posible que su consulta DNS sea la causa del tiempo de espera?
Lo siento por utilizar un hilo antiguo, pero creo que algo que se dijo anteriormente puede ser incorrecto / engañoso.
Por lo que puedo decir. Timeout NO es el tiempo de conexión, es el tiempo total permitido durante toda la vida de HttpWebRequest y la respuesta. Prueba:
Lo puse:
.Timeout=5000
.ReadWriteTimeout=32000
El tiempo de conexión y publicación de HttpWebRequest tomó 26ms
pero la llamada subsiguiente HttpWebRequest.GetResponse () excedió el tiempo de espera en 4974ms, lo que demuestra que 5000 ms era el límite de tiempo para toda la solicitud de envío / obtener un conjunto de llamadas de respuesta.
No verifiqué si la resolución del nombre DNS se midió como parte del tiempo, ya que esto es irrelevante para mí, ya que nada de esto funciona de la manera que realmente necesito que funcione; mi intención era agotar el tiempo de conexión más rápido cuando me conectaba a sistemas que no aceptaban conexiones como las mostradas fallando durante la fase de conexión de la solicitud.
Por ejemplo: estoy dispuesto a esperar 30 segundos en una solicitud de conexión que tenga una posibilidad de devolver un resultado, pero solo deseo grabar 10 segundos esperando enviar una solicitud a un host que se está portando mal.
No importa lo que hayamos intentado, no logramos obtener el tiempo de espera por debajo de 21 segundos cuando el servidor que estábamos verificando no funcionaba.
Para solucionar este problema, combinamos una comprobación de TcpClient para ver si el dominio estaba activo, seguido de una comprobación por separado para ver si la URL estaba activa.
public static bool IsUrlAlive(string aUrl, int aTimeoutSeconds)
{
try
{
//check the domain first
if (IsDomainAlive(new Uri(aUrl).Host, aTimeoutSeconds))
{
//only now check the url itself
var request = System.Net.WebRequest.Create(aUrl);
request.Method = "HEAD";
request.Timeout = aTimeoutSeconds * 1000;
var response = (HttpWebResponse)request.GetResponse();
return response.StatusCode == HttpStatusCode.OK;
}
}
catch
{
}
return false;
}
private static bool IsDomainAlive(string aDomain, int aTimeoutSeconds)
{
try
{
using (TcpClient client = new TcpClient())
{
var result = client.BeginConnect(aDomain, 80, null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(aTimeoutSeconds));
if (!success)
{
return false;
}
// we have connected
client.EndConnect(result);
return true;
}
}
catch
{
}
return false;
}
Creo que el problema es que WebRequest
mide el tiempo solo después de que se realiza realmente la solicitud. Si envía varias solicitudes a la misma dirección, ServicePointManager
acelerará sus solicitudes y solo enviará tantas conexiones simultáneas como el valor del ServicePoint.ConnectionLimit
correspondiente que de forma predeterminada obtiene el valor de ServicePointManager.DefaultConnectionLimit
. El host CLR de la aplicación establece esto en 2, el host ASP en 10. Entonces, si tiene una aplicación multiproceso que envía múltiples solicitudes al mismo host, solo dos se colocan en el cable, el resto se pone en cola.
No he investigado esto con una evidencia concluyente sobre si esto es realmente lo que sucede, pero en un proyecto similar tuve cosas horribles hasta que ServicePoint
limitación de ServicePoint
.
Otro factor a considerar es el tiempo de búsqueda de DNS. Una vez más, mi creencia no está respaldada por pruebas sólidas, pero creo que WebRequest
no cuenta el tiempo de búsqueda de DNS con el tiempo de espera de la solicitud. El tiempo de búsqueda de DNS puede aparecer como factor de tiempo muy grande en algunas implementaciones.
Y sí, debe codificar su aplicación en WebRequest.BeginGetRequestStream
(para POST
con contenido) y WebRequest.BeginGetResponse
(para GET
y POSTS
s). Las llamadas sincrónicas no se escalarán (no entraré en detalles por qué, pero sí tengo pruebas fehacientes). De todos modos, el problema de ServicePoint
es ortogonal a esto: el comportamiento de cola también ocurre con las llamadas asincrónicas.