HttpWebRequest ist extrem langsam!

74

Ich verwende eine Open Source-Bibliothek, um eine Verbindung zu meinem Webserver herzustellen. Ich war besorgt, dass der Webserver extrem langsam lief, und habe dann versucht, einen einfachen Test in Ruby durchzuführen, und diese Ergebnisse erhalten

Ruby-Programm: 2,11 Sekunden für 10 HTTP-GETs

Ruby-Programm: 18,13 Sekunden für 100 HTTP-GETs

C # -Bibliothek: 20,81 Sekunden für 10 HTTP-GETs

C # -Bibliothek: 36847,46 Sekunden für 100 HTTP-GETs

Ich habe ein Profil erstellt und festgestellt, dass das Problem diese Funktion ist:

private HttpWebResponse GetRawResponse(HttpWebRequest request) {
  HttpWebResponse raw = null;
  try {
    raw = (HttpWebResponse)request.GetResponse(); //This line!
  }
  catch (WebException ex) {
    if (ex.Response is HttpWebResponse) {
      raw = ex.Response as HttpWebResponse;
    }
  }
  return raw;
}

Es dauert mehr als 1 Sekunde, bis die markierte Zeile von selbst abgeschlossen ist, während das Ruby-Programm, das 1 Anfrage stellt, 0,3 Sekunden dauert. Ich mache auch alle diese Tests auf 127.0.0.1, so dass die Netzwerkbandbreite kein Problem ist.

Was könnte diese enorme Verlangsamung verursachen?

AKTUALISIEREN

Überprüfen Sie die geänderten Benchmark-Ergebnisse. Ich habe tatsächlich mit 10 GETs und nicht mit 100 getestet und die Ergebnisse aktualisiert.

Earlz
quelle
Verwenden Sie aus Neugier dasselbe HttpWebRequest-Objekt für alle GETs? Ich denke, die Verlangsamung ist nur der erste Handschlag. Versuchen Sie, dasselbe HttpWebRequest-Objekt wiederzuverwenden.
BFree
Schließen Sie beim Profilieren den ersten Anruf aus? Ich habe manchmal bemerkt, dass die FireWall (oder etwas anderes) den ersten Anruf um Sekunden verlangsamen kann. (Alt: mehr als 100 Anfragen messen, bleibt das Verhältnis gleich?)
Henk Holterman
Ich glaube, dass bei jedem Aufruf ein neues Anforderungsobjekt erstellt wird. Welche Art von "Handshake" muss jedoch für HTTP durchgeführt werden? Es ist ein ziemlich vereinfachtes Protokoll
Earlz
1
@Henk, ich habe gerade versucht, 500 Anfragen zu machen und gab nach 5 Minuten auf ...
Earlz

Antworten:

175

Was ich als Hauptschuldigen bei langsamen Webanfragen herausgefunden habe, ist die Proxy-Eigenschaft. Wenn Sie diese Eigenschaft vor dem Aufrufen der GetResponse-Methode auf null setzen, überspringt die Abfrage den Autodetect-Schritt des Proxys:

request.Proxy = null;
using (var response = (HttpWebResponse)request.GetResponse())
{
}

Die automatische Abfrage des Proxys dauerte bis zu 7 Sekunden, bevor die Antwort zurückgegeben wurde. Es ist etwas ärgerlich, dass diese Eigenschaft für das HttpWebRequest-Objekt standardmäßig aktiviert ist.

James Roland
quelle
2
James, du rockst. Ich hätte dies schließlich über Wireshark gefunden, aber buchstäblich Sekunden nach dem Lesen dieses Beitrags war unsere WebDAV-Leistung über hundert schneller. Ich vermute, unser Sharpoint-Server hat das gleiche Problem
Paul Lockwood
1
Ich habe das gleiche Problem (die Antwort dauert mehr oder weniger 1 Sekunde), aber das Setzen von WebRequest.DefaultWebProxy = null löst mein Problem nicht. Irgendeine andere Idee?
Cristiano Ghersi
Es tut uns leid, dass Sie den Thread hochgefahren haben, aber was passiert, wenn die Adresse, auf die Sie zugreifen möchten, nur über den Standard-Proxy verfügbar ist?
Florian F.
3
Ich vermute, der Schlüssel hier ist eher, den GetResponseAnruf in eine usingDirektive zu packen, da die Anzahl der Verbindungen begrenzt ist und Sie warten müssen, bis sie eine Zeitüberschreitung aufweisen, wenn Sie sie nicht ordnungsgemäß schließen.
Nicolas78
1
Das hat bei mir funktioniert, vielen Dank. Die Verwendung der GetResponse-Methode von HTTPWebRequest dauerte mehr als 5 Minuten, um zu antworten. Es wird versucht, den Systemproxy standardmäßig abzurufen, und es tritt eine Zeitüberschreitung auf. Das manuelle Aufrufen von GetSystemWebProxy hatte das gleiche Problem auf meinem Computer. Aber das Setzen von Proxy auf Null machte es wieder schnell.
Bobasaurus
21

Dies hängt möglicherweise damit zusammen, dass Sie mehrere Verbindungen gleichzeitig öffnen. Standardmäßig ist die maximale Anzahl offener HTTP-Verbindungen auf zwei festgelegt. Versuchen Sie, dies zu Ihrer .config-Datei hinzuzufügen, und prüfen Sie, ob es hilft:

<system.net>
  .......
  <connectionManagement>
    <add address="*" maxconnection="20"/>
  </connectionManagement>
</system.net>
Manu
quelle
Dies ist derjenige, der für mich arbeitet. Mein Szenario ist wie folgt
smwikipedia
10

Ich hatte ein ähnliches Problem mit einem VB.Net MVC-Projekt.
Auf meinem PC (Windows 7) dauerte es weniger als 1 Sekunde, um die Seitenanforderungen zu erfüllen, auf dem Server (Windows Server 2008 R2) dauerte es für jede Seitenanforderung mehr als 20 Sekunden.

Ich habe versucht, den Proxy auf Null zu setzen

  System.Net.WebRequest.DefaultWebProxy = Nothing
  request.Proxy = System.Net.WebRequest.DefaultWebProxy

Und Ändern der Konfigurationsdatei durch Hinzufügen

 <system.net>
   .......
   <connectionManagement>
     <add address="*" maxconnection="20"/>
   </connectionManagement>
 </system.net>

Dies reduzierte die langsamen Seitenanforderungszeiten auf dem Server immer noch nicht. Am Ende bestand die Lösung darin, die Option "Einstellungen automatisch erkennen" in den IE-Optionen auf dem Server selbst zu deaktivieren . (Wählen Sie unter Extras -> Internetoptionen die Registerkarte Verbindungen. Klicken Sie auf die Schaltfläche LAN-Einstellungen.)

Unmittelbar nachdem ich diese Browseroption auf dem Server deaktiviert hatte, sanken alle Seitenanforderungszeiten von mehr als 20 Sekunden auf unter 1 Sekunde.

Jamieb
quelle
8

Ich bemerkte eine Verlangsamung ähnlich der OP in diesem Bereich, die beim Erhöhen der MaxConnections etwas besser wurde.

ServicePointManager.DefaultConnectionLimit = 4;

Nachdem diese Anzahl von WebRequests erstellt wurde, kamen die Verzögerungen zurück.

Das Problem in meinem Fall war, dass ich einen POST anrief und mich nicht um die Antwort kümmerte, also nichts aufnahm oder etwas damit machte. Leider schwebte die WebRequest so lange herum, bis das Zeitlimit überschritten wurde.

Die Lösung bestand darin, die Antwort aufzunehmen und einfach zu schließen.

WebRequest webRequest = WebRequest.Create(sURL);
webRequest.Method = "POST";
webRequest.ContentLength = byteDataGZ.Length;
webRequest.Proxy = null;
using (var requestStream = webRequest.GetRequestStream())
{
    requestStream.WriteTimeout = 500;
    requestStream.Write(byteDataGZ, 0, byteDataGZ.Length);
    requestStream.Close();
}
// Get the response so that we don't leave this request hanging around
WebResponse response = webRequest.GetResponse();
response.Close();
Tonycoupland
quelle
Interessant. Daran hatte ich nicht gedacht, weil man normalerweise auch etwas aus der Antwort herausholen möchte. Ich bin mir ziemlich sicher, dass dies nicht mein eigentliches Problem war, aber dennoch interessant
Earlz
Dies hat mein Problem nicht gelöst, aber es hat mich dazu gebracht, meine Verwendung zu überprüfen, HttpWebResponseund ich habe eines gefunden, das ich nicht geschlossen habe.
Craig Silver
3

Verwenden Sie einen anderen Computer als localhost und verwenden Sie dann WireShark, um zu sehen, was wirklich über das Kabel läuft.

Wie andere gesagt haben, kann es eine Reihe von Dingen sein. Ein Blick auf die Dinge auf TCP-Ebene sollte ein klares Bild ergeben.

Kervin
quelle
3

Ich weiß nicht, wie genau ich zu dieser Problemumgehung gekommen bin. Ich hatte noch keine Zeit für Nachforschungen, also liegt es an euch. Es gibt einen Parameter, den ich wie folgt verwendet habe (im Konstruktor meiner Klasse, bevor das HTTPWebRequest-Objekt instanziiert wurde):

System.Net.ServicePointManager.Expect100Continue = false;

Ich weiß nicht genau warum, aber jetzt sehen meine Anrufe viel schneller aus.

user753746
quelle
Es kann in der Tat hilfreich sein, wenn Sie POST / PUT (siehe Dokumentation ).
Styxxy
Es kann auch langsamer werden, wenn die Möglichkeit besteht, dass der Server die PUT / POST-Daten ablehnt (dh wenn Sie dies deaktivieren, wissen Sie erst, nachdem der PUT / POST-Vorgang abgeschlossen wurde, dass der Anruf abgelehnt wurde).
BrainSlugs83
1

Ich weiß, dass dies ein alter Thread ist, aber ich habe den ganzen Tag mit langsamem HttpWebRequest verloren und jede bereitgestellte Lösung ohne Glück ausprobiert. Jede Anfrage nach einer Adresse dauerte mehr als eine Minute.

Schließlich lag ein Problem mit meiner Antivirus-Firewall (Eset) vor . Ich verwende eine Firewall mit interaktivem Modus, aber Eset wurde irgendwie vollständig ausgeschaltet. Das führte dazu, dass die Anfrage für immer andauerte. Nach dem Einschalten von Eset und dem Ausführen der Anforderung wird eine Firewall-Meldung angezeigt, und nach der Bestätigung wird die Anforderung weniger als eine Sekunde lang ausgeführt .

Šemsudin Tafilović
quelle
Danke für den Tipp. Ich habe Windows-Firewalls völlig vergessen und es stellt sich heraus, dass die erste Anfrage Äonen dauern kann, wenn sie nicht richtig konfiguriert ist. Esp. Wenn es in den Gewinndialogen "ausgeschaltet" ist und der Dienst noch ausgeführt wird.
Xan-Kun Clark-Davis
0

Ich habe alle hier beschriebenen Lösungen ohne Glück ausprobiert, der Anruf dauerte ca. 5 Minuten.

Was war das Problem: Ich brauchte dieselbe Sitzung und offensichtlich dieselben Cookies (Anfrage auf demselben Server), also habe ich die Cookies von Request.Cookies in WebRequest.CookieContainer neu erstellt . Die Reaktionszeit betrug ca. 5 Minuten.

Meine Lösung: Den Cookie-Code und bam auskommentiert! Der Anruf dauerte weniger als eine Sekunde.

mihai_omega
quelle
0

Für mich bestand das Problem darin, dass ich LogMeIn Hamachi installiert hatte - ironischerweise, um dasselbe Programm aus der Ferne zu debuggen, das dann diese extreme Langsamkeit zeigte.

Zu Ihrer Information, das Deaktivieren des Hamachi-Netzwerkadapters reichte nicht aus, da der Windows-Dienst den Adapter anscheinend wieder aktiviert.

Auch das erneute Herstellen einer Verbindung zu meinem Hamachi-Netzwerk hat das Problem nicht gelöst. Nur das Deaktivieren des Adapters (durch Deaktivieren des Windows-Dienstes LogMeIn Hamachi) oder vermutlich das Deinstallieren von Hamachi hat das Problem für mich behoben.

Ist es möglich zu fragen, ob HttpWebRequestSie über einen bestimmten Netzwerkadapter ausgehen möchten?

Craig Silver
quelle
0

Für mich HttpWebRequestbetrug das Aufrufen einer API im Durchschnitt 40 ms und das Aufrufen einer API auf einem Server durchschnittlich 270 ms. Der Anruf über Postman betrug in beiden Umgebungen durchschnittlich 40 ms. Keine der Lösungen in diesem Thread hat für mich einen Unterschied gemacht.

Dann fand ich diesen Artikel, in dem der Nagle-Algorithmus erwähnt wurde:

Der Nagle-Algorithmus erhöht die Netzwerkeffizienz, indem die Anzahl der über das Netzwerk gesendeten Pakete verringert wird. Dies wird erreicht, indem auf dem Client eine Verzögerung von bis zu 200 Millisekunden eingeführt wird, wenn kleine Datenmengen in das Netzwerk geschrieben werden . Die Verzögerung ist eine Wartezeit für zusätzliche Daten, die möglicherweise geschrieben werden. Neue Daten werden demselben Paket hinzugefügt.

Das Einstellen ServicePoint.UseNagleAlgorithmauf falsewar die Magie, die ich brauchte, es machte einen großen Unterschied und die Leistung auf dem Server ist jetzt fast identisch mit der lokalen.

var webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ServicePoint.Expect100Continue = false;
webRequest.ServicePoint.UseNagleAlgorithm = false; // <<<this is the important bit

Beachten Sie, dass dies bei kleinen Datenmengen bei mir funktioniert hat. Wenn Ihre Anfrage jedoch große Datenmengen enthält, kann es sinnvoll sein, dieses Flag abhängig von der Größe der Anfrage / der erwarteten Größe der Antwort abhängig zu machen.

Dämoncodemonkey
quelle
0

In meinem Fall AspxAutoDetectCookieSupport=1zum Code hinzufügen und das Problem wurde gelöst

Uri target = new Uri("http://payroll");

string responseContent;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(target);

request.CookieContainer = new CookieContainer();         
request.CookieContainer.Add(new Cookie("AspxAutoDetectCookieSupport", "1") { Domain = target.Host });

using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
    using (Stream responseStream = response.GetResponseStream())
    {
        using (StreamReader sr = new StreamReader(responseStream))
            responseContent = sr.ReadToEnd();
    }
}
Ashkufaraz
quelle
-1

Wir hatten das gleiche Problem mit der Web-App. Wir haben 5 Sekunden auf die Antwort gewartet. Wenn wir den Benutzer in applicationPool auf IIS in networkService ändern, kam die Antwort weniger als 1 Sekunde an

user2693593
quelle