2010-12-07 15 views
9

Ich schreibe eine Anwendung, um zu messen, wie schnell ich Webseiten mit C# herunterladen kann. Ich gebe eine Liste mit eindeutigen Domainnamen an, dann spawne ich eine Anzahl von Threads und führe HTTPWebRequests aus, bis die Liste der Domains verbraucht ist. Das Problem ist, dass egal wie viele Threads ich verwende, ich nur etwa 3 Seiten pro Sekunde bekomme.Concurrency Limit auf HttpWebRequest

Ich entdeckte, dass die System.Net.ServicePointManager.DefaultConnectionLimit 2 ist, aber ich hatte den Eindruck, dass dies mit der Anzahl der Verbindungen pro Domäne verbunden ist. Da jede Domäne in der Liste eindeutig ist, sollte dies kein Problem sein.

Dann habe ich festgestellt, dass die GetResponse() -Methode den Zugriff von allen anderen Prozessen blockiert, bis die WebResponse geschlossen ist: http://www.codeproject.com/KB/IP/Crawler.aspx#WebRequest, ich habe keine anderen Informationen im Web gefunden, um diesen Anspruch zu untermauern, jedoch habe ich eine HTTP-Anfrage implementiert Steckdosen, und ich bemerkte eine deutliche Beschleunigung (4x bis 6x).

Also meine Fragen: weiß jemand genau, wie die HttpWebRequest Objekte funktionieren?, Gibt es eine Workaround neben dem, was oben erwähnt wurde? Oder gibt es Beispiele für Hochgeschwindigkeits-Crawler in C# irgendwo geschrieben?

+0

Sie können das Verbindungslimit pro Domäne konfigurieren, aber das Verbindungslimit ist standardmäßig global. https://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx – Todd

Antwort

8

Haben Sie versucht, die asynchronen Methoden wie BeginGetResponse() zu verwenden?

Wenn Sie .net 4.0 verwenden, möchten Sie vielleicht diesen Code ausprobieren. Im Grunde verwende ich Aufgaben 1000 Anfragen auf einer bestimmte Website zu machen (ich benutze diese Lasttests von App auf meiner Dev-Maschine zu tun, und ich sehe keine Grenzen als solche, da mein app diese Anforderungen in schnellen Folge ist zu sehen)

public partial class Form1 : Form 
    { 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 
     for (int i = 0; i < 1000; i++) 
     { 
     var webRequest = WebRequest.Create(textBox1.Text); 
     webRequest.GetReponseAsync().ContinueWith(t => 
     { 
      if (t.Exception == null) 
      { 
      using (var sr = new StreamReader(t.Result.GetResponseStream())) 
      { 
       string str = sr.ReadToEnd(); 
      } 
      } 
      else 
      System.Diagnostics.Debug.WriteLine(t.Exception.InnerException.Message); 
     }); 
     } 
    } 
    } 

    public static class WebRequestExtensions 
    { 
    public static Task<WebResponse> GetReponseAsync(this WebRequest request) 
    { 
     return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null); 
    } 
    } 

Da die Arbeitslast hier I/O-gebunden ist, ist das Erstellen von Threads zum Ausführen der Aufgabe nicht erforderlich und kann die Leistung beeinträchtigen. Wenn Sie die Async-Methoden für die WebClient-Klasse verwenden, verwenden Sie I/O-Completion-Ports und sind daher leistungsfähiger und weniger ressourcenhungrig.

3

Sie sollten die Methode BeginGetResponse verwenden, die nicht blockiert und asynchron ist.

Beim Umgang mit I/O-gebundener Asynchronität wird dieser Thread immer noch blockiert, wenn Sie einen Thread für die E/A-Arbeit generieren, der darauf wartet, dass die Hardware (in diesem Fall die Netzwerkkarte) antwortet. Wenn Sie die integrierte BeginGetResponse verwenden, wird dieser Thread sie nur auf der Netzwerkkarte in die Warteschlange stellen und steht dann für weitere Arbeiten zur Verfügung. Wenn die Hardware fertig ist, wird es Sie benachrichtigen, zu welchem ​​Zeitpunkt Ihr Rückruf aufgerufen wird.

1

Ich möchte darauf hinweisen, dass BeginGetResponse Methode nicht vollständig asynchron ist: (von MSDN)

Die BeginGetResponse Methode einige synchrone Setup-Aufgaben erfordert abzuschließen (DNS-Auflösung, Proxy-Erkennung, und TCP-Socket Verbindung), bevor diese Methode asynchron wird. Daher sollte diese Methode nie auf einem Benutzerschnittstellen-Thread aufgerufen werden, da dies einige Zeit dauern kann, in der Regel mehrere Sekunden.