web-dev-qa-db-fra.com

Réglage du délai d'expiration de la connexion HttpWebRequest en C #

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?"

45
JYelton

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 POSTs avec contenu) et WebRequest.BeginGetResponse (pour GETs et POSTSs). 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.

57
Remus Rusanu

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.

35
TechSavvySam

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.)

19
JYelton

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?

14
GBegen

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;
}
8
Karl Glennon