HttpClient async-requests nicht den Abschluss für große batch-geschickt in eine Schleife

Ich glaube, ich habe es geschafft, einen test, der zeigt dieses problem wiederholbar, zumindest auf meinem system. Diese Frage bezieht sich auf HttpClient verwendet wird, für eine schlechte Endpunkt (nonexistant Endpunkt, das Ziel ist).

Das problem ist, dass die Anzahl der abgeschlossenen Aufgaben greift zu kurz, die gesamte, in der Regel etwa ein paar. Ich nicht Anfragen, die nicht arbeiten, aber das nur die Ergebnisse in der app nur dort hängen, wenn die Ergebnisse abgewartet werden.

Bekomme ich das folgende Ergebnis form den test-code unten:

Elapsed: 237.2009884 Sekunden.
Aufgaben in der batch-array: 8000 Abgeschlossene Aufgaben : 7993

Wenn ich batchsize zu 8 anstelle von 8000, die es vollendet. Für 8000 es Staus auf der WhenAll .

Ich Frage mich, ob andere Leute das gleiche Ergebnis erhalten, wenn ich etwas falsch machen, und wenn dies scheint ein Fehler zu sein.

using System;
using System.Diagnostics;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace CustomArrayTesting
{

    ///<summary>
    ///Problem: a large batch of async http requests is done in a loop using HttpClient, and a few of them never complete
    ///</summary>
    class ProgramTestHttpClient
    {
        static readonly int batchSize = 8000; //large batch size brings about the problem

        static readonly Uri Target = new Uri("http://localhost:8080/BadAddress");

        static TimeSpan httpClientTimeout = TimeSpan.FromSeconds(3);  //short Timeout seems to bring about the problem.

        ///<summary>
        ///Sends off a bunch of async httpRequests using a loop, and then waits for the batch of requests to finish.
        ///I installed asp.net web api client libraries Nuget package.
        ///</summary>
        static void Main(String[] args)
        {
            httpClient.Timeout = httpClientTimeout; 

            stopWatch = new Stopwatch();
            stopWatch.Start();


            //this timer updates the screen with the number of completed tasks in the batch (See timerAction method bellow Main)
            TimerCallback _timerAction = timerAction;
            TimerCallback _resetTimer = ResetTimer;
            TimerCallback _timerCallback = _timerAction + _resetTimer;

            timer = new Timer(_timerCallback, null, TimeSpan.FromSeconds(1), Timeout.InfiniteTimeSpan);
            //

            for (int i = 0; i < batchSize; i++)
            {
                Task<HttpResponseMessage> _response = httpClient.PostAsJsonAsync<Object>(Target, new Object());//WatchRequestBody()

                Batch[i] = _response;
            }

            try
            {
                Task.WhenAll(Batch).Wait();
            }
            catch (Exception ex)
            {

            }

            timer.Dispose();
            timerAction(null);
            stopWatch.Stop();


            Console.WriteLine("Done");
            Console.ReadLine();
        }

        static readonly TimeSpan timerRepeat = TimeSpan.FromSeconds(1);

        static readonly HttpClient httpClient = new HttpClient();

        static Stopwatch stopWatch;

        static System.Threading.Timer timer;

        static readonly Task[] Batch = new Task[batchSize];

        static void timerAction(Object state)
        {
            Console.Clear();
            Console.WriteLine("Elapsed: {0} seconds.", stopWatch.Elapsed.TotalSeconds);
            var _tasks = from _task in Batch where _task != null select _task;
            int _tasksCount = _tasks.Count();

            var _completedTasks = from __task in _tasks where __task.IsCompleted select __task;
            int _completedTasksCount = _completedTasks.Count();

            Console.WriteLine("Tasks in batch array: {0}       Completed Tasks : {1} ", _tasksCount, _completedTasksCount);

        }

        static void ResetTimer(Object state)
        {
            timer.Change(timerRepeat, Timeout.InfiniteTimeSpan);
        }
    }
}

Manchmal ist es einfach abstürzt, vor dem beenden mit einer Zugriffsverletzung nicht behandelte Ausnahme. Der call-stack sagt nur:

>   mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode = 1225, uint numBytes = 0, System.Threading.NativeOverlapped* pOVERLAP = 0x08b38b98) 
    [Native to Managed Transition]  
    kernel32.dll!@BaseThreadInitThunk@12()  
    ntdll.dll!___RtlUserThreadStart@8()     
    ntdll.dll!__RtlUserThreadStart@8()  

Meisten der Zeit es nicht Abstürzen, aber eben nie beendet, wartet auf die whenall. In jedem Fall sind die folgenden erste-chance-Ausnahmen geworfen werden, für jede Anfrage:

A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
A first chance exception of type 'System.Net.WebException' occurred in System.dll
A first chance exception of type 'System.AggregateException' occurred in mscorlib.dll
A first chance exception of type 'System.ObjectDisposedException' occurred in System.dll

Machte ich den debugger beenden, die auf das Objekt entsorgt Ausnahme, und habe diese call stack:

>   System.dll!System.Net.Sockets.NetworkStream.UnsafeBeginWrite(byte[] buffer, int offset, int size, System.AsyncCallback callback, object state) + 0x136 bytes    
    System.dll!System.Net.PooledStream.UnsafeBeginWrite(byte[] buffer, int offset, int size, System.AsyncCallback callback, object state) + 0x19 bytes  
    System.dll!System.Net.ConnectStream.WriteHeaders(bool async = true) + 0x105 bytes   
    System.dll!System.Net.HttpWebRequest.EndSubmitRequest() + 0x8a bytes    
    System.dll!System.Net.HttpWebRequest.SetRequestSubmitDone(System.Net.ConnectStream submitStream) + 0x11d bytes  
    System.dll!System.Net.Connection.CompleteConnection(bool async, System.Net.HttpWebRequest request = {System.Net.HttpWebRequest}) + 0x16c bytes  
    System.dll!System.Net.Connection.CompleteConnectionWrapper(object request, object state) + 0x4e bytes   
    System.dll!System.Net.PooledStream.ConnectionCallback(object owningObject, System.Exception e, System.Net.Sockets.Socket socket, System.Net.IPAddress address) + 0xf0 bytes 
    System.dll!System.Net.ServicePoint.ConnectSocketCallback(System.IAsyncResult asyncResult) + 0xe6 bytes  
    System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken) + 0x65 bytes    
    System.dll!System.Net.ContextAwareResult.Complete(System.IntPtr userToken) + 0x92 bytes 
    System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object result, System.IntPtr userToken) + 0xa6 bytes  
    System.dll!System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) + 0x98 bytes 
    mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) + 0x6e bytes    
    [Native to Managed Transition]

Ausnahme Nachricht war:

{"Cannot access a disposed object.\r\nObject name: 'System.Net.Sockets.NetworkStream'."}    System.Exception {System.ObjectDisposedException}

Beachten Sie die Beziehung, dass unbehandelte Zugriffsverletzung-Ausnahme, die ich nur selten zu sehen.

So, es scheint, dass HttpClient ist nicht robust ist, wenn das Ziel down ist. Ich mache das auf windows 7 32 übrigens.

  • Dies erfordert eine beträchtlich lange Zeit zu laufen auf meinem Rechner (892 Sekunden), aber alle 8000 Aufgaben abgeschlossen sind. Vielleicht sind Sie knapp an ephemeral ports?
  • Vielen Dank für die läuft es. Vielleicht hat das etwas damit zu tun. Finden Sie heraus, vielleicht über mich. Ich werde führen Sie es wieder nach einem Neustart.
  • Bleibt trotzdem das Problem für die größeren Chargen. Ich habe die temporären Ports situation mit einem Skript aus dem technet, und es scheint in Ordnung; nicht sehr viele ports verwendet. Bekomme ich eine unbehandelte Fehler manchmal. Ich aktualisiere die Frage mit ein paar mehr Infos.
  • Warum auf der Erde würden Sie wollen, zu laufen, 8000 parallele Aufgaben?
  • 8000 Aufgaben nichts, getestet habe ich die task parallel library für Hunderte von tausenden von Aufgaben (die nichts tun) und es funktionierte perfekt, ohne viel Speicher. Für die web-Anfrage, die ich machen möchte 8000 parallel, wenn jeder nimmt sich 1 Sekunden lang, um zu vermeiden, dass Sie die 8000 Sekunden.
  • Dieser wurde vor kurzem veröffentlicht. Es kann etwas Licht auf die situation, wenn Sie erfassen ein dump, eine minute und verwenden Sie !whttp.
  • jedes Ding auf dieser Seite entfernt wurde...

InformationsquelleAutor Elliot | 2013-11-08
Schreibe einen Kommentar