Je crois qu'après de longues recherches et recherches, j'ai découvert que ce que je veux faire est probablement mieux servi en établissant une connexion asynchrone et en la terminant après le délai d'attente souhaité ... Mais je vais continuer et demander quand même!
Extrait rapide de code:
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.
J'ai une méthode HttpWebRequest
qui se trouve dans une application multi-thread, dans laquelle je me connecte à un grand nombre de serveurs Web d'entreprise. Dans les cas où le serveur ne répond pas, la HttpWebRequest.GetResponse()
met environ 20 secondes à expirer, même si j'ai spécifié un délai d'expiration de seulement 5 secondes. Dans l'intérêt de passer à travers les serveurs sur un intervalle régulier, je veux ignorer ceux qui prennent plus de 5 secondes pour se connecter.
La question est donc la suivante: "Existe-t-il un moyen simple de spécifier/diminuer un délai de connexion pour une WebRequest ou HttpWebRequest?"
Je crois que le problème est que le WebRequest
ne mesure le temps qu'après que la demande soit réellement faite. Si vous soumettez plusieurs demandes à la même adresse, le ServicePointManager
limitera vos demandes et ne soumettra en fait autant de connexions simultanées que la valeur de la --- ServicePoint.ConnectionLimit
qui obtient par défaut la valeur de ServicePointManager.DefaultConnectionLimit
. L'application CLR Host définit ceci à 2, ASP Host à 10. Donc, si vous avez une application multithread qui soumet plusieurs requêtes au même hôte, seules deux sont réellement placées sur le câble, les autres sont mises en file d'attente vers le haut.
Je n'ai pas recherché cela pour une preuve concluante de savoir si c'est vraiment ce qui se passe, mais sur un projet similaire, j'ai eu des choses horribles jusqu'à ce que je supprime la limitation ServicePoint
.
Un autre facteur à considérer est le temps de recherche DNS. Encore une fois, ma croyance n'est-elle pas étayée par des preuves tangibles, mais je pense que le WebRequest
ne pas compte le temps de recherche DNS par rapport au délai d'expiration de la demande . Le temps de recherche DNS peut apparaître comme un facteur de temps très important sur certains déploiements.
Et oui, vous devez coder votre application autour du WebRequest.BeginGetRequestStream
(pour POST
s avec contenu) et WebRequest.BeginGetResponse
(pour GET
s et POSTS
s). Les appels synchrones ne seront pas mis à l'échelle (je n'entrerai pas dans les détails pourquoi, mais pour lesquels j'ai des preuves tangibles). Quoi qu'il en soit, le problème ServicePoint
est orthogonal à cela: le comportement de mise en file d'attente se produit également avec les appels asynchrones.
Désolé d'avoir cloué sur un vieux fil, mais je pense que quelque chose qui a été dit ci-dessus peut être incorrect/trompeur.
D'après ce que je peux dire .Timeout n'est PAS le temps de connexion, c'est le temps TOTAL autorisé pour toute la durée de vie de HttpWebRequest et de la réponse. Preuve:
Je mets:
.Timeout=5000
.ReadWriteTimeout=32000
Le temps de connexion et de publication pour HttpWebRequest a pris 26 ms
mais l'appel suivant HttpWebRequest.GetResponse () a expiré en 4974 ms, prouvant ainsi que les 5000 ms étaient la limite de temps pour l'ensemble des appels d'envoi/réponse.
Je n'ai pas vérifié si la résolution du nom DNS était mesurée dans le temps car cela ne me concerne pas car rien de tout cela ne fonctionne comme j'en ai vraiment besoin pour travailler - mon intention était de temporiser plus rapidement lors de la connexion à des systèmes qui n'acceptaient pas les connexions comme indiqué par leur échec pendant la phase de connexion de la demande.
Par exemple: je suis prêt à attendre 30 secondes sur une demande de connexion qui a une chance de renvoyer un résultat, mais je veux seulement graver 10 secondes en attendant d'envoyer une demande à un hôte qui se comporte mal.
Quelque chose que j'ai trouvé plus tard qui m'a aidé, c'est le .ReadWriteTimeout
propriété. Ceci, en plus du .Timeout
la propriété semblait enfin réduire le temps que les threads passeraient à essayer de télécharger à partir d'un serveur problématique. L'heure par défaut pour .ReadWriteTimeout
est de 5 minutes, ce qui était beaucoup trop long pour mon application.
Il me semble donc:
.Timeout
= temps passé à essayer d'établir une connexion (sans compter le temps de recherche) .ReadWriteTimeout
= temps passé à lire ou à écrire des données après l'établissement de la connexion
Plus d'informations: propriété HttpWebRequest.ReadWriteTimeout
Modifier:
Selon le commentaire de @ KyleM, la propriété Timeout
est pour la tentative de connexion entière, et sa lecture sur MSDN montre:
Le délai d'attente est le nombre de millisecondes pendant lequel une demande synchrone ultérieure effectuée avec la méthode GetResponse attend une réponse et la méthode GetRequestStream attend un flux. Le délai d'expiration s'applique à l'ensemble de la demande et de la réponse, et non individuellement aux appels de méthode GetRequestStream et GetResponse. Si la ressource n'est pas renvoyée dans le délai imparti , la demande lève une WebException avec la propriété Status définie sur WebExceptionStatus.Timeout.
(Je souligne.)
Dans la documentation de la propriété HttpWebRequest.Timeout:
Une requête DNS (Domain Name System) (DNS) peut prendre jusqu'à 15 secondes pour revenir ou expirer. Si votre demande contient un nom d'hôte qui nécessite une résolution et que vous définissez Timeout sur une valeur inférieure à 15 secondes, il peut s'écouler 15 secondes ou plus avant qu'une exception WebException soit levée pour indiquer un délai d'expiration sur votre demande.
Est-il possible que votre requête DNS soit la cause du timeout?
Peu importe ce que nous avons essayé, nous n'avons pas réussi à obtenir un délai inférieur à 21 secondes lorsque le serveur que nous vérifions était en panne.
Pour contourner ce problème, nous avons combiné une vérification TcpClient pour voir si le domaine était vivant, suivie d'une vérification distincte pour voir si l'URL était active
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;
}