2016-08-19 2 views
2

Je crée un flooder de page Web pour tester la sécurité sur mes serveurs. J'ai ce code:C# Threading Ram Utilisation

static void Main(string[] args) 
{ 
    string url; 
    int times; 
    Console.WriteLine("Inserisci l'indirizzo(pagina specifica) da floodare"); 
    url = Console.ReadLine(); 
    Console.WriteLine("Quante volte?"); 
    times = Int32.Parse(Console.ReadLine()); 
    System.Net.ServicePointManager.DefaultConnectionLimit = 350; 
    var block = new System.Threading.Tasks.Dataflow.ActionBlock<int>(async i => 
    { 
     try 
     { 
      await Work(i, url); 
     } 
     catch (Exception e) 
     { 

     } 
    }, new System.Threading.Tasks.Dataflow.ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 350 }); 

    for(int i = 0; i < times; i++) 
    { 
     block.Post(i); 
    } 
    block.Complete(); 

    Task.WaitAll(block.Completion); 
} 
private static async Task<bool> Work(int i, string url) 
{ 
    if (i % 1000 == 0) 
     Console.WriteLine(i); 

    new WebClient().UploadValuesTaskAsync(url, new System.Collections.Specialized.NameValueCollection()); 

    //await Task.Delay(1); 
    return true; 
} 
} 

Il fait son travail très bien, mais il a un problème. Si je place le nombre de demandes vraiment élevé (comme 200 milliers ou plus), il utilise beaucoup de RAM. À 1,3 millions de requêtes, il utilise 3,3 Go de RAM. J'ai essayé de tout disposer, de libérer le bélier de quelque façon que ce soit mais il semble que la variable de bloc stocke chaque fil jusqu'à ce qu'il finisse. Comment puis-je resoudre ceci?

+0

Pourquoi n'attendez-vous pas 'UploadValuesTaskAsync' dans' Work() '? Avez-vous essayé de disposer du 'WebClient'? – aghidini

+0

oui j'ai essayé. Je ne veux pas attendre qu'il soit terminé, je veux juste l'exécuter, je ne me soucie pas du résultat – LifeRewind

+0

@LifeRewind, avez-vous essayé le profileur de mémoire? Quels objets ne sont pas collectés par GC? –

Répondre

1

Ce que j'ai trouvé. Vous utilisez WebClient, mais ne le jetez pas. WebClient leaks

méthode de travail devrait être quelque chose comme ceci:

private static async Task<bool> Work(int i, string url) 
{ 
    if (i % 1000 == 0) 
     Console.WriteLine(i); 

    // Must dispose to avoid leaks 
    using (var wc = new WebClient()) 
    { 
     await wc.UploadValuesTaskAsync(url, new System.Collections.Specialized.NameValueCollection()); 
    } 
    //await Task.Delay(1); 
    return true; 
} 

J'Updated ai utilisé HttpWebRequest et méthode asynchrone BeginGetResponse pour résoudre votre problème. Code complet est https://dotnetfiddle.net/ZecQku:

namespace HttpWebRequest_HighIntesive 
{ 
    using System; 
    using System.Diagnostics; 
    using System.Net; 
    using System.Threading; 

    class ThreadParam 
    { 
     public int RequestsCount { get; private set; } 
     public CountdownEvent CountdownEvent { get; private set; } 
     public ThreadParam(int requestsCount, CountdownEvent countdownEvent) 
     { 
      RequestsCount = requestsCount; 
      CountdownEvent = countdownEvent; 
     } 
    } 
    class FinistRequestParam 
    { 
     public CountdownEvent CountdownEvent { get; private set; } 
     public HttpWebRequest HttpWebRequest { get; private set; } 
     public FinistRequestParam(CountdownEvent countdownEvent, HttpWebRequest httpWebRequest) 
     { 
      CountdownEvent = countdownEvent; 
      HttpWebRequest = httpWebRequest; 
     } 
    } 
    public class Program 
    { 
     static Uri _uri; 
     static volatile int _numberOfFinishedRequests; 
     static double _prevMemoryMb = 0; 

     public static int Main(string[] args) 
     { 
      int numOfRequests; 
      Console.Write("Enter URL(full format, for example, http://google.ru): "); 
      var url = Console.ReadLine(); 
      if (!Uri.TryCreate(url, UriKind.RelativeOrAbsolute, out _uri)){ 
       Console.WriteLine("Invalid URL. Exiting"); return -1; 
      } 
      Console.Write("Enter number of requests: "); 
      numOfRequests = Int32.Parse(Console.ReadLine()); 

      Console.WriteLine(""); 
      DoParallelRequests(numOfRequests); 

      Console.WriteLine("Finished. Press 'Enter' to quit"); 
      Console.ReadLine(); 
      return 0; 
     } 
     private static void DoParallelRequests(int numOfRequests) 
     { 
      // Play with DefaultConnectionLimit 
      // Increasing this value will increase speed, but also increase memory consumption 
      System.Net.ServicePointManager.DefaultConnectionLimit = 350; 
      Console.WriteLine("DefaultConnectionLimit: {0}", System.Net.ServicePointManager.DefaultConnectionLimit); 
      int threadCnt = Environment.ProcessorCount; 
      Console.WriteLine("Num of threads which creates HttpWebRequest: {0}", threadCnt); 
      // Initialize CountDownEvent with numOfRequests 
      CountdownEvent countDownOnTimes = new CountdownEvent(numOfRequests); 
      // Create timer for statistics 
      using (var timer = new Timer(TimerStatisticHanlder, Stopwatch.StartNew(), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(5))) 
      { 
       // Create thread array 
       Thread[] threads = new Thread[threadCnt]; 
       // Initialize each thread and start it 
       for (int i = 0; i < threads.Length; i++) 
       { 
        threads[i] = new Thread(ThreadMethod); 
        // HACK Hope numOfRequests % threadCnt == 0 (evenly divisible) 
        // Start thread 
        threads[i].Start(new ThreadParam(numOfRequests/threadCnt, countDownOnTimes)); 
       } 
       // Will wait untill all request processed 
       countDownOnTimes.Wait(); 
      } 
     } 
     static void TimerStatisticHanlder(object obj) 
     { 
      Stopwatch sw = obj as Stopwatch; 
      // Calculate average speed 
      var aveageSpeed = Math.Round(_numberOfFinishedRequests/sw.Elapsed.TotalSeconds, 2); 
      // Get total memory 
      var totalMemoryMb = Math.Round((double)GC.GetTotalMemory(false)/1024/1024); 
      // Calculate memory delta 
      var memoryDeltaMb = totalMemoryMb - _prevMemoryMb; 
      // Print out statistics 
      Console.WriteLine("{0} Processed requests: {1}, Average speed: {2} requests per/s, Used memory: {3} Mbytes, Memory delta: {4}", DateTime.Now.ToString("HH:mm:ss"), _numberOfFinishedRequests, aveageSpeed, totalMemoryMb, memoryDeltaMb); 
      // Store total memory for delta calculation 
      _prevMemoryMb = totalMemoryMb; 
     } 
     private static void ThreadMethod(object state) 
     { 
      var threadParam = state as ThreadParam; 
      for (int i = 0; i <= threadParam.RequestsCount; i++) 
      { 
       // Create HttpWebRequest 
       HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_uri); 
       // Start it asynchronous 
       request.BeginGetResponse(new AsyncCallback(FinishRequest), new FinistRequestParam(threadParam.CountdownEvent, request)); 
      } 
     } 
     private static void FinishRequest(IAsyncResult result) 
     { 
      var reqParam = result.AsyncState as FinistRequestParam; 
      var request = reqParam.HttpWebRequest; 
      try 
      { 
       // Just end response 
       HttpWebResponse response = request.EndGetResponse(result) as HttpWebResponse; 
       // Release all resources 
       response.GetResponseStream().Dispose(); 
       response.Close(); 
       (request as IDisposable).Dispose(); 
      } 
      catch { } // Don't care about exceptions 

      // Mark yet another request finished 
      reqParam.CountdownEvent.Signal(); 
      // Increment total number of finished requests 
      Interlocked.Increment(ref _numberOfFinishedRequests); 
     } 
    } 
} 

écran d'application test: Scree of test app

+0

c'est ok mais si vous mettez un attente, il perd l'objectif principal, d'inonder. Mais si je prends l'awat, même en utilisant votre code. il utilise beaucoup de RAM. Je pense que le problème est dans la façon dont j'appelle la fonction. Je pense que la variable de bloc stocke tout jusqu'à ce qu'il soit fini. Avez-vous une suggestion? – LifeRewind

+0

Merci l'homme! Je vais l'essayer! – LifeRewind

1

En Work vous n'attendez pas l'IO pour terminer. Cela rend Work complet extrêmement rapidement. Cela désactive effectivement la limitation de débit que vous vouliez (MaxDegreeOfParallelism = 350).

Les travaux démarrent beaucoup plus rapidement qu'ils ne sont terminés. C'est pourquoi les éléments de travail s'accumulent et consomment de la mémoire.

Attendez la fin de l'E/S.