2013-11-08 2 views
5

Я думаю, что мне удалось сделать тест, который показывает эту проблему повторяемо, по крайней мере, в моей системе. Этот вопрос относится к HttpClient, который используется для плохой конечной точки (неисключительная конечная точка, цель не указана).Запросы async HttpClient не завершаются для большой партии, отправленной в цикле

Проблема в том, что количество завершенных задач не соответствует общей, как правило, примерно для нескольких. Я не возражаю, что запросы не работают, но это просто приводит к тому, что приложение просто висит там, когда ожидаются результаты.

я получаю следующий результат формирует код теста:

Прошедшее: 237.2009884 секунд. Задачи в пакетном массиве: 8000 Завершенные задачи: 7993

Если я установил batchsize в 8 вместо 8000, он завершается. Для 8000 он застревает на WhenAll.

Интересно, получают ли другие люди одинаковый результат, если я что-то делаю неправильно, и если это кажется ошибкой.

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

Иногда он просто выходит из строя, прежде чем заканчивается исключение из-за нарушения доступа. Стек вызовов просто говорит:

> mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode = 1225, uint numBytes = 0, System.Threading.NativeOverlapped* pOVERLAP = 0x08b38b98) 
    [Native to Managed Transition] 
    [email protected]@12() 
    [email protected]()  
    [email protected]() 

Большую часть времени она не врезаться, но просто никогда не заканчивает ждать на whenall. В любом случае после первой же возможности исключения выбрасываются для каждого запроса:

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 

Я сделал отладчик остановку на объекте, расположенный за исключением, и получил этот стек вызовов:

> 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] 

Сообщение об исключении было:

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

Обратите внимание на отношение к этому необработанному исключению нарушения доступа, которое я редко вижу.

Итак, кажется, что HttpClient не является надежным, когда цель направлена ​​вниз. Я делаю это на Windows 7 32, кстати.

+1

Это занимает значительно больше времени для работы на моей машине (892 секунды), но все 8000 задач завершены. Возможно, у вас малое количество на эфемерных портах? –

+0

Спасибо, что запустили его. Может быть, это имеет к этому какое-то отношение. Выяснение может быть вне меня. Я перезапущу его снова после перезагрузки. – Elliot

+0

Проблема по-прежнему остается для больших партий. Я проверил ситуацию с эфемерным портом, используя скрипт из technet, и это кажется прекрасным; не очень много используемых портов. Иногда я получаю необработанную ошибку. Я уточняю вопрос с дополнительной информацией. – Elliot

ответ

0

Ваша информация об исключении теряется в задаче WhenAll. Вместо того, чтобы использовать это, попробуйте следующее:

Task aggregateTask = Task.Factory.ContinueWhenAll(
    Batch, 
    TaskExtrasExtensions.PropagateExceptions, 
    TaskContinuationOptions.ExecuteSynchronously); 

aggregateTask.Wait(); 

При этом используется метод PropagateExceptions расширения от Parallel Extensions Дополнительно образец кода, чтобы гарантировать, что информация об исключении из задач в пакетном режиме, не теряются:

/// <summary>Propagates any exceptions that occurred on the specified tasks.</summary> 
/// <param name="tasks">The Task instances whose exceptions are to be propagated.</param> 
public static void PropagateExceptions(this Task [] tasks) 
{ 
    if (tasks == null) throw new ArgumentNullException("tasks"); 
    if (tasks.Any(t => t == null)) throw new ArgumentException("tasks"); 
    if (tasks.Any(t => !t.IsCompleted)) throw new InvalidOperationException("A task has not completed."); 
    Task.WaitAll(tasks); 
} 
+0

Thankyou. Кроме того, я только что узнал, как остановить первые исключения, поэтому я снова обновляю свой вопрос. – Elliot

3

Я просмотрел источник HttpClient, используя отражатель. Насколько я понимаю, для синхронно выполняемой части операции (когда она стартовала), по-видимому, нет никакого тайм-аута для возвращаемой задачи. Существует некоторая реализация тайм-аута, которая вызывает Abort() в объекте HttpWebRequest, но опять же они, похоже, пропустили отмену тайм-аута или сбой возвращенной задачи с этой стороны функции async. Возможно, что-то есть на стороне обратного вызова, но иногда обратный вызов, вероятно, «пропадает», что приводит к тому, что возвращаемая задача никогда не завершается.

Я отправил вопрос с вопросом, как добавить тайм-аут для любой задачи, и отвечающему дал это очень хорошее решение (здесь в качестве метода расширения):

public static Task<T> WithTimeout<T>(this Task<T> task, TimeSpan timeout) 
{ 
    var delay = task.ContinueWith(t => t.Result 
     , new CancellationTokenSource(timeout).Token); 
    return Task.WhenAny(task, delay).Unwrap(); 
} 

Так, называя HttpClient, как это должно предотвратить любые «Задача испортилась» от никогда не заканчивается:

Task<HttpResponseMessage> _response = httpClient.PostAsJsonAsync<Object>(Target, new Object()).WithTimeout<HttpResponseMessage>(httpClient.Timeout); 

пара больше вещей, которые я думаю, сделанные запросы реже пропадают: 1. Увеличение тайма-аута от 3s до 30-х лет сделали все задачи, закончить в программе, Я отправил с этим вопросом. 2. Увеличение количества одновременных подключений, разрешенных с помощью, например, System.Net.ServicePointManager.DefaultConnectionLimit = 100;

2

Я столкнулся с этим вопросом при поиске в Google для решения подобной проблемы из WCF. Эта серия исключений точно такая же, как я вижу. В итоге через тонну расследования я обнаружил ошибку в HttpWebRequest, которую использует HttpClient. HttpWebRequest попадает в плохое состояние и отправляет только HTTP-заголовки. Затем он сидит в ожидании ответа, который никогда не будет отправлен.

Я поднял билет с Microsoft Connect, который можно найти здесь: https://connect.microsoft.com/VisualStudio/feedback/details/1805955/async-post-httpwebrequest-hangs-when-a-socketexception-occurs-during-setsocketoption

Специфика в билете, но это требует асинхронной POST вызов от HttpWebRequest к не-LOCALHOST машины. Я воспроизвел его на Windows 7 в .Net 4.5 и 4.6. Неудавшийся вызов SetSocketOption, который вызывает SocketException, только терпит неудачу в Windows 7 при тестировании.

Для нас параметр UseNagleAlgorithm вызывает вызов SetSocketOption, но мы не можем его избежать, поскольку WCF отключает UseNagleAlgorithm, и вы не можете его остановить. В WCF он отображается как тайм-аут. Очевидно, это не так здорово, поскольку мы тратим 60-х, ничего не ожидая.

+0

У меня такая же ошибка. У меня есть веб-API ASP.NET, который делает асинхронные HTTP-сообщения другой службе. Время от времени он получает «Stuck», и никакие HTTP-запросы не выполняются должным образом, пока я не перезапущу пул приложений. Я использую 'wait client.PostAsJsonAsync()' – Zapnologica

 Смежные вопросы

  • Нет связанных вопросов^_^