Потоковая передача данных с конечной точки ASP. NET Core 3.1 без больших объемов памяти. - PullRequest
1 голос
/ 06 апреля 2020

Редактировать: у меня был компонент промежуточного программного обеспечения для диагностики, который извлекал ответы в файл трассировки. Это был виновник. Итак, если вы обнаружите это, потому что у вас есть та же проблема, что и у меня: проверьте ваше промежуточное ПО!

Итак, при вызове конечной точки Web API я собираю огромные объемы данных (> 100 МБ ) из различных источников данных в потоковом асинхронном режиме. Я хочу перенаправить эти данные клиенту в потоковом режиме.

Для этой цели я построил свой собственный IActionResultExecutor<T>, чтобы обобщить это, поскольку у нас есть несколько таких конечных точек.

I однако заметили, что весь ответ кэшируется в памяти, прежде чем он фактически отправляется клиенту. Фигово. Очевидно, что я делаю что-то не так, но я не могу понять, что я делаю неправильно!

Исполнитель ExecuteAsync выглядит так:

public async Task ExecuteAsync(ActionContext context, AsyncStreamResult result)
{
   var bufferingFeature = context.HttpContext.Features.Get<IHttpResponseBodyFeature>();
   if (bufferingFeature != null)
      bufferingFeature.DisableBuffering();

   context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
   context.HttpContext.Response.ContentType = "application/json; charset=utf-8";

   var cancellationToken = context.HttpContext.RequestAborted;
   await context.HttpContext.Response.StartAsync(cancellationToken);
   await context.HttpContext.Response.WriteAsync("[", cancellationToken);
   bool seenFirstItem = false;

   await foreach (var item in result.Data) {
      if (seenFirstItem)
         await context.HttpContext.Response.WriteAsync(",", cancellationToken);

      await context.HttpContext.Response.BodyWriter.WriteAsync(item.JsonBytes, cancellationToken);

      seenFirstItem = true;
   }

   await context.HttpContext.Response.WriteAsync("]", cancellationToken);
}

Я вижу, как все В Visual Studio процесс выделяет много памяти линейным способом. Я также вижу, что curl не получает никаких данных, пока запрос не будет завершен. Тогда все приходит в одном go. Ирония в том, что curl сообщает, что кодировка передачи данных chunked. Это один большой кусок! Некоторые из потоков данных превышают 100 МБ, и я не могу позволить себе громоздкие модули в моем кластере k8s только по этой причине. На мой взгляд, эта операция должна генерировать кучу объектов Gen 0, но ничего, с чем G C не мог бы справиться. Ссылочные объекты должны учитываться в килобайтах!

Я пробовал посыпать некоторые await context.HttpContext.Response.BodyWriter.FlushAsync(cancellationToken), но, похоже, ничего не изменилось.

Что я делаю не так?

1 Ответ

0 голосов
/ 06 апреля 2020

Проблема решена: у меня в конвейере было некоторое промежуточное программное обеспечение для ведения журнала отладки, которое включало буферизацию для отслеживания ответов, отправляемых клиентам. DOH!

...