2010-12-07 2 views
9

J'écris une application pour mesurer à quelle vitesse je peux télécharger des pages Web en utilisant C#. Je fournis une liste de noms de domaine uniques, puis je génère X nombre de threads et j'effectue HTTPWebRequests jusqu'à ce que la liste des domaines ait été utilisée. Le problème est que peu importe le nombre de threads que j'utilise, je ne reçois que 3 pages par seconde.Limite de simultanéité sur HttpWebRequest

J'ai découvert que System.Net.ServicePointManager.DefaultConnectionLimit est 2, mais j'avais l'impression que cela est lié au nombre de connexions par domaine. Étant donné que chaque domaine de la liste est unique, cela ne devrait pas poser de problème.

Ensuite, j'ai trouvé que la méthode GetResponse() bloque l'accès de tous les autres processus jusqu'à la fermeture de WebResponse: http://www.codeproject.com/KB/IP/Crawler.aspx#WebRequest, je n'ai trouvé aucune autre information sur le web pour soutenir cette revendication, cependant j'ai implémenté une requête HTTP en utilisant prises, et j'ai remarqué une accélération significative (4x à 6x). Donc mes questions: quelqu'un sait exactement comment les objets HttpWebRequest fonctionnent ?, existe-t-il une solution de contournement en dehors de ce qui a été mentionné ci-dessus ?, ou existe-t-il des exemples de crawlers web à haute vitesse écrits en C# n'importe où?

+0

Vous pouvez configurer la limite de connexion par domaine, mais par défaut, la limite de connexion est globale. https://msdn.microsoft.com/en-us/library/fb6y0fyc.aspx – Todd

Répondre

8

Avez-vous essayé d'utiliser les méthodes async telles que BeginGetResponse()?

Si vous utilisez .net 4.0, vous pouvez essayer ce code. Essentiellement, j'utiliser des tâches pour faire 1000 requêtes sur un site spécifique (je l'utiliser pour faire des tests de charge de l'application sur ma machine dev et je ne vois pas de limites en tant que telle que mon application est de voir ces demandes en succession rapide)

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); 
    } 
    } 

Étant donné que la charge de travail est liée aux E/S, il n'est pas nécessaire de générer des threads pour effectuer le travail et cela pourrait en effet nuire aux performances. L'utilisation des méthodes Async sur la classe WebClient utilise des ports d'achèvement d'E/S et sera donc beaucoup plus performant et moins gourmand en ressources.

3

Vous devez utiliser la méthode BeginGetResponse qui ne bloque pas et est asynchrone. En cas d'asynchronisme lié aux E/S, tout simplement parce que vous créez un thread pour faire fonctionner les E/S, ce thread sera toujours bloqué en attendant que le matériel (dans ce cas la carte réseau) réponde. Si vous utilisez le BeginGetResponse intégré, alors ce thread va juste le mettre en file d'attente sur la carte réseau, et sera alors disponible pour faire plus de travail. Lorsque le matériel est terminé, il vous en informe, à quel moment votre rappel sera appelé.

1

Je voudrais souligner que la méthode BeginGetResponse est pas complètement asynchrone: (de MSDN)

La méthode BeginGetResponse nécessite certaines tâches de configuration synchrones pour terminer (résolution DNS, détection proxy et socket TCP connexion, par exemple) avant que cette méthode devienne asynchrone. Par conséquent, cette méthode ne doit jamais être appelée sur un thread d'interface utilisateur (UI) car cela peut prendre un certain temps, généralement plusieurs secondes.