2016-08-17 6 views
1

Моя команда недавно реорганизовала службу веб-API, чтобы переместить часть повторяющегося кода в статические методы. Один метод связан с извлечением загруженного файла из запроса. Метод работает в модульном тестировании, но под нагрузкой бросает исключения. Часть кода была найдена в сообщении SO, но я обеспокоен тем, что в целом мы не используем его правильно. Вот код:Ошибка чтения WebAPI MIME partpart body part

internal static string ExtractFile(HttpRequestMessage request) 
{ 
    if (request.Content.IsMimeMultipartContent()) 
    { 
     string uploadRoot = ServiceHelper.GetUploadDirectoryPath(); 

     var provider = new MultipartFormDataStreamProvider(uploadRoot); 

     try 
     { 
      Task.Factory 
      .StartNew(() => provider = request.Content.ReadAsMultipartAsync(provider).Result, 
       CancellationToken.None, 
       TaskCreationOptions.LongRunning, // guarantees separate thread 
       TaskScheduler.Default) 
      .Wait(); 
     } 
     catch(System.AggregateException ae) 
     { 
      if(log.IsErrorEnabled) 
      { 
       foreach(var ex in ae.InnerExceptions) 
       { 
        log.Error("ReadAsMultipartAsync task error.", ex); 
       } 
      } 

      var errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "An error occurred while extracting the uploaded file from the request."); 
      throw new HttpResponseException(errorResponse); 
     } 

     var fileData = provider.FileData.First(); 

     var localName = fileData.LocalFileName; 
     var content = File.ReadAllText(localName); 

     if (log.IsDebugEnabled) 
     { 
      var embeddedName = fileData.Headers.ContentDisposition.FileName; 
      log.DebugFormat("File {0} was successfully uploaded as '{1}'.", embeddedName, localName); 
     } 

     return content; 
    } 
    else 
    { 
     log.Error("Invalid request received. Request must be in a multipart/form-data request."); 

     var errorResponse = request.CreateErrorResponse(HttpStatusCode.InternalServerError, "Request must be a multipart/form-data request and contain one file."); 
     throw new HttpResponseException(errorResponse); 
    } 
} 

Прогулка через бревна, я вижу ошибки, подобные этим:

System.IO.IOException: Ошибка чтения MIME многочастному части тела. ---> System.IO.IOException ---> System.Net.HttpListenerException: Операция ввода/вывода была прервана из-за выхода потока или по запросу приложения

HttpListenerRequest расположенного

System.IO .IOException: Ошибка чтения MIME частичной части тела. ---> System.IO.IOException ---> System.Net.HttpListenerException: была предпринята операция по несуществующему сетевому подключению

Эта веб-служба работает как автономная служба Windows OWIN. Загрузка файлов небольшая (от 3k до 4k).

Я не могу воссоздать проблему с одной загрузкой. Клиент, который разговаривает с сервисом, использует задачи для отправки файлов, но обычно он запускает более 4 или 5 задач одновременно. Моя команда и я относительно новичок в задачах .NET. Один из разработчиков задается вопросом, действительно ли параметр TaskCreationOptions.LongRunning наносит боль больше, чем помогает. Какие-либо предложения?

Update:

Я попытался переключения вне Task.Factory код с этим:

var task = Task.Run(async() => await request.Content.ReadAsMultipartAsync(provider)); 
task.Wait(); 
provider = task.Result; 

Я до сих пор некоторые проблемы, но это, кажется, работает лучше. Не знаю, почему.

+0

Может проверить конфигурацию Owin для тайм-аута/keep- живые настройки? Возможно, это может быть вызвано медленными/пятнистыми соединениями? – MiltoxBeyond

ответ

0

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

Я предполагаю, что ваш метод вызывается из входящего веб-запроса api. Есть ли причина, по которой вы не делаете это «асинхронно»? Это самый простой способ структурировать его и имеет наименьшую вероятность блокировки позже из-за плохого/дефектного кода (, который действительно легко сделать, если вы только начинаете с Заданий и асинхронно/ожидаете).

// new signature (I changed the name and prefixed Async which is a common convention) 
internal static async Task<string> ExtractFileAsync(HttpRequestMessage request) 
{ 
    if (request.Content.IsMimeMultipartContent()) 
    { 
     string uploadRoot = ServiceHelper.GetUploadDirectoryPath(); 

     var provider = new MultipartFormDataStreamProvider(uploadRoot); 

     await request.Content.ReadAsMultipartAsync(provider); 
     /* rest of code */ 
    } 
} 

// calling method example from some Web API Controller 
public async Task<IHttpActionResult> Post(CancellationToken token) 
{ 
    var result = await ExtractFileAsync(Request).ConfigureAwait(true); 
    /*some other code*/ 
} 
+0

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

1

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

Вот как я обновил код клиента:

// New private field in my client class 
private SemaphoreSlim _semaphore = new SemaphoreSlim(Settings.Default.MaxConcurrentRequests, Settings.Default.MaxConcurrentRequests); 

// Added continuation to Polly tasks 
foreach (var file in files) 
{ 
    var task = retryPolicy.ExecuteAsync(() => SendIllustrationRequest(file)); 
    task.ContinueWith((x) => _semaphore.Release()); 

    _transactionTasks.Add(task); 
} 

Task.WaitAll(_transactionTasks.ToArray()); 

// Added semaphore wait method to the start of the SendIllustrationRequest(file) method 
_semaphore.Wait(); 

Этот метод работает хорошо для меня, так как я использую Polly и Polly начинает сразу же выполнить поставленную задачу.Если у вас есть возможность создавать задачи без их немедленного запуска, есть и другие варианты.

+1

Приятно видеть этот Дуг! В интересах будущих читателей: Polly v5.0 (выпущенный в ближайшее время и доступный теперь в alpha on nuget) имеет встроенную политику для такого рода дросселирования параллелизма: https://github.com/App-vNext/Polly/wiki/Bulkhead. Используя https://github.com/App-vNext/Polly/wiki/PolicyWrap, он может быть объединен с другими политиками Polly в единый 'PolicyWrap' –

0

Я была такая же проблема, она была решена путем добавления следующего кода в web.config

<system.web> 
 
    <httpRuntime maxRequestLength="30000000" /> 
 
</system.web> 
 

 
<system.webServer> 
 
    <security> 
 
     <requestFiltering> 
 
     <requestLimits maxAllowedContentLength="30000000" /> 
 
     </requestFiltering> 
 
    </security> 
 
</system.webServer>

Источник: http://stackoverflow.com/questions/20942636/webapi-cannot-parse-multipart-form-data-post

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