Редактировать: у меня был компонент промежуточного программного обеспечения для диагностики, который извлекал ответы в файл трассировки. Это был виновник. Итак, если вы обнаружите это, потому что у вас есть та же проблема, что и у меня: проверьте ваше промежуточное ПО!
Итак, при вызове конечной точки 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)
, но, похоже, ничего не изменилось.
Что я делаю не так?