wake-up-neo.com

Anpassen des HttpWebRequest-Verbindungszeitlimits in C #

Ich glaube, nach langem Nachforschen und Suchen habe ich herausgefunden, dass es wahrscheinlich besser ist, eine asynchrone Verbindung aufzubauen und sie nach dem gewünschten Timeout zu beenden, wenn ich das möchte. Aber ich werde trotzdem weitermachen und fragen!

Schneller Code-Ausschnitt:

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.

Ich habe eine HttpWebRequest -Methode, die sich in einer Multithread-Anwendung befindet, in der ich eine Verbindung zu einer großen Anzahl von Unternehmenswebservern herstelle. In Fällen, in denen der Server nicht antwortet, dauert die Zeitüberschreitung für HttpWebRequest.GetResponse() ungefähr 20 Sekunden, obwohl ich eine Zeitüberschreitung von nur 5 Sekunden angegeben habe. Um in regelmäßigen Abständen durch die Server zu gelangen, möchte ich diejenigen überspringen, deren Verbindungsaufbau länger als 5 Sekunden dauert.

Die Frage lautet also: "Gibt es eine einfache Möglichkeit, ein Verbindungszeitlimit für eine WebRequest oder eine HttpWebRequest festzulegen/zu verringern?"

45
JYelton

Ich glaube , dass das Problem darin besteht, dass WebRequest die Zeit erst misst, nachdem die Anfrage tatsächlich gestellt wurde. Wenn Sie mehrere Anfragen an dieselbe Adresse senden, drosselt das ServicePointManager Ihre Anfragen und sendet tatsächlich nur so viele gleichzeitige Verbindungen, wie der Wert des entsprechenden ServicePoint.ConnectionLimit welches standardmäßig den Wert von ServicePointManager.DefaultConnectionLimit . Der CLR-Host der Anwendung setzt dies auf 2, ASP Host auf 10. Wenn Sie also eine Multithread-Anwendung haben, die mehrere Anforderungen an denselben Host sendet, werden nur zwei tatsächlich auf der Leitung platziert, der Rest wird in die Warteschlange gestellt oben.

Ich habe dies nicht zu einem schlüssigen Beweis dafür recherchiert, ob dies wirklich der Fall ist, aber bei einem ähnlichen Projekt hatte ich Dinge, die schrecklich waren, bis ich die ServicePoint -Einschränkung aufhob.

Ein weiterer zu berücksichtigender Faktor ist die DNS-Suchzeit. Auch hier ist meine Überzeugung nicht durch harte Beweise gestützt, aber ich denke, das WebRequest zählt nicht die DNS-Suchzeit gegen das Anforderungszeitlimit . Die DNS-Nachschlagezeit kann bei einigen Bereitstellungen als sehr großer Zeitfaktor angezeigt werden.

Und ja, Sie müssen Ihre App um das WebRequest.BeginGetRequestStream (für POSTs mit Inhalt) und WebRequest.BeginGetResponse (für GETs und POSTSs). Synchrone Anrufe werden nicht skaliert (ich werde nicht auf Details eingehen, warum, aber ich habe harte Beweise dafür). Jedenfalls ist das Problem ServicePoint orthogonal dazu: Das Warteschlangenverhalten tritt auch bei asynchronen Aufrufen auf.

57
Remus Rusanu

Es tut mir leid, dass ich an einem alten Thread festgehalten habe, aber ich denke, dass etwas, das oben gesagt wurde, falsch/irreführend sein kann.

Nach allem, was ich sagen kann. Timeout ist NICHT die Verbindungszeit, es ist die GESAMTE Zeit, die für die gesamte Lebensdauer der HttpWebRequest und der Antwort zulässig ist. Beweis:

Ich setze:

.Timeout=5000
.ReadWriteTimeout=32000

Die Verbindungs- und Post-Zeit für das HttpWebRequest dauerte 26 ms

der nachfolgende Aufruf von HttpWebRequest.GetResponse () lief jedoch nach 4974 ms ab, was beweist, dass die 5000 ms das Zeitlimit für die gesamte Gruppe von Sende-/Antwortaufrufen waren.

Ich habe nicht überprüft, ob die DNS-Namensauflösung als Teil der Zeit gemessen wurde, da dies für mich irrelevant ist, da dies nicht so funktioniert, wie ich es wirklich brauche akzeptierten keine Verbindungen, wie von ihnen angezeigt, und scheiterten während der Verbindungsphase der Anforderung.

Beispiel: Ich bin bereit, 30 Sekunden auf eine Verbindungsanforderung zu warten, bei der die Wahrscheinlichkeit besteht, dass ein Ergebnis zurückgegeben wird. Ich möchte jedoch nur 10 Sekunden warten, bis eine Anforderung an einen Host gesendet wird, der sich nicht ordnungsgemäß verhält.

35
TechSavvySam

Etwas, das ich später gefunden habe und das geholfen hat, ist das .ReadWriteTimeout Eigentum. Dies zusätzlich zu der .Timeout -Eigenschaft schien die Zeit, die Threads für den Download von einem problematischen Server benötigen, endlich zu verkürzen. Die Standardzeit für .ReadWriteTimeout ist 5 Minuten, was für meine Bewerbung viel zu lang war.

So scheint es mir:

.Timeout = Zeitaufwand für den Verbindungsaufbau (ohne Nachschlagezeit) .ReadWriteTimeout = Zeitaufwand für den Versuch, Daten zu lesen oder zu schreiben, nachdem die Verbindung hergestellt wurde

Weitere Informationen: HttpWebRequest.ReadWriteTimeout-Eigenschaft

Bearbeiten:

Gemäß dem Kommentar von @ KyleM gilt die Eigenschaft Timeout für den gesamten Verbindungsversuch, und das Nachlesen bei MSDN zeigt Folgendes:

Das Zeitlimit gibt die Anzahl der Millisekunden an, die eine nachfolgende synchrone Anforderung mit der GetResponse-Methode auf eine Antwort wartet und die GetRequestStream-Methode auf einen Stream. Das Zeitlimit gilt für die gesamte Anforderung und Antwort, nicht einzeln für die Methodenaufrufe GetRequestStream und GetResponse. Wenn die Ressource nicht innerhalb des Zeitlimits zurückgegeben wird löst die Anforderung eine WebException aus, deren Status-Eigenschaft auf WebExceptionStatus.Timeout festgelegt ist.

(Betonung meiner.)

19
JYelton

Aus der Dokumentation der HttpWebRequest.Timeout-Eigenschaft:

Es kann bis zu 15 Sekunden dauern, bis eine DNS-Abfrage (Domain Name System) zurückgegeben wird oder eine Zeitüberschreitung auftritt. Wenn Ihre Anfrage einen Hostnamen enthält, für den eine Auflösung erforderlich ist, und Sie das Zeitlimit auf einen Wert von weniger als 15 Sekunden festlegen, kann es 15 Sekunden oder länger dauern, bis eine WebException ausgelöst wird, um ein Zeitlimit für Ihre Anfrage anzugeben.

Ist es möglich, dass Ihre DNS-Abfrage die Ursache für das Timeout ist?

14
GBegen

Egal, was wir versucht haben, wir konnten es nicht schaffen, das Timeout unter 21 Sekunden zu halten, als der Server, den wir überprüften, ausfiel.

Um dies zu umgehen, haben wir eine TcpClient-Prüfung kombiniert, um festzustellen, ob die Domäne aktiv war, gefolgt von einer separaten Prüfung, um festzustellen, ob die URL aktiv war

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