ASP.NET Web API 2 - StreamContent чрезвычайно медленный

Мы перенесли проект из WCF в Web API (SelfHost), и во время процесса мы заметили огромный спад при обслуживании веб-приложения. Теперь 40-50 секунд против 3 секунд ранее.

Я воспроизвел проблему в простом консольном приложении, добавив различные Nacet Pacakges для AspNet.WebApi и OwinSelfHost со следующим контроллером:

var stream = new MemoryStream();
using (var file = File.OpenRead(filename))
{
    file.CopyTo(stream);
}
stream.Position = 0;

var response = Request.CreateResponse(System.Net.HttpStatusCode.OK);

/// THIS IS FAST
response.Content = new ByteArrayContent(stream.ToArray());
/// THIS IS SLOW
response.Content = new StreamContent(stream);

response.Content.Headers.ContentType = new MediaTypeHeaderValue(System.Web.MimeMapping.GetMimeMapping(filename));            
response.Content.Headers.ContentLength = stream.Length;

Как видно из кода, единственным отличием является использование StreamContent (slooooow) и ByteArrayContent.

Приложение размещено на машине Win10 и доступно с моего ноутбука. Fiddler показывает, что для получения одного 1 МБ файла с сервера на мой ноутбук с использованием StreamContent требуется 14 секунд, а ByteArrayContent меньше 1 с.

Также обратите внимание, что полный файл считывается в память, чтобы показать, что единственным отличием является класс содержимого.

Странная вещь заключается в том, что кажется, что ее передача сама по себе медленная. Сервер отвечает на заголовки быстро/немедленно, но для получения данных требуется много времени, как показано в информации о времени Fiddler:

GotResponseHeaders: 07:50:52.800
ServerDoneResponse: 07:51:08.471

Полная информация о времени:

== TIMING INFO ============
ClientConnected:    07:50:52.238
ClientBeginRequest: 07:50:52.238
GotRequestHeaders:  07:50:52.238
ClientDoneRequest:  07:50:52.238
Determine Gateway:  0ms
DNS Lookup:         0ms
TCP/IP Connect:     15ms
HTTPS Handshake:    0ms
ServerConnected:    07:50:52.253
FiddlerBeginRequest:07:50:52.253
ServerGotRequest:   07:50:52.253
ServerBeginResponse:07:50:52.800
GotResponseHeaders: 07:50:52.800
ServerDoneResponse: 07:51:08.471
ClientBeginResponse:07:51:08.471
ClientDoneResponse: 07:51:08.471

Overall Elapsed:    0:00:16.233

Кто-нибудь знает, что происходит под капотом, который может объяснить разницу в поведении?

Ответ 1

Решением моей проблемы для самостоятельного хостинга OWIN был размер буфера StreamContent. По умолчанию конструктор StreamContent использует значение по умолчанию 0x1000, 4Kb. В гигабитной сети передача 26 Мб файла занимает ~ 7 минут, чтобы завершить со скоростью ~ 60 Кбит/с.

 const int BufferSize = 1024 * 1024;
 responseMessage = new HttpResponseMessage();
 responseMessage.Content = new StreamContent(fileStream, BufferSize);

Изменение значения bufferSize до 1Mb займет всего несколько секунд, чтобы завершить загрузку.

[EDIT] В StreamContent SerializeToStreamAsync делает StreamToStreamCopy, в соответствии с этой ссылкой производительность будет отличаться. Подходящее значение, вероятно, составляет 80K.

Ответ 2

У меня такая же проблема, и я думаю, что это связано с самим хостингом Owin. Я просто создал образец приложения Asp.net и разместил его на IIS. В этом случае он работал, как ожидалось.

Результаты моей тестовой системы при загрузке файла размером 80 МБ:

  • с Streamcontent и SelfHosting: ~ 20 минут
  • с ByteArrayContent и Selfhosting: < 30 секунд
  • с Streamcontent и IIS Hosting: < 30 секунд

Либо есть параметр конфигурации в ASP.net, который мне не хватает в моем самообслуживаемом проекте, либо есть ошибка в коде самообслуживания owin, я думаю.

Ответ 3

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