Дневник сертифицированного .NET разработчика. Заметки, советы, новости из мира .NET и C#.
Для связи: @SBenzenko
Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Post #3324
1.57K
День 2778. #ЗаметкиНаПолях
Потоковая Передача JSON в .NET. Начало
Допустим, у вас есть конечная точка, которая возвращает список: все заказы за последний квартал или поток показаний датчиков. Код выглядит нормально:
Для пары сотен строк это не заметно. При паре десятков тысяч происходит скачок потребления памяти, начинает работать сборщик мусора, и время до получения первого байта растёт, как и нагрузка на процесс при увеличении количества одновременных запросов. Решение в том, чтобы прекратить буферизацию. Сериализуем элемент, отдаём, переходим к следующему.
System.Text.Json может передавать IAsyncEnumerable<T> потоком начиная с .NET 6. Платформа выдаёт JSON-массив потоком, а не буферизирует его:
Это решает проблему использования памяти. Сервер хранит один заказ за раз, а не весь список. Вывод – JSON-массив:
Это вполне приемлемо для браузера, вызывающего функцию
Окончание следует…
Источник: https://thecodeman.net/posts/streaming-json-in-dotnet-with-json-lines
Потоковая Передача JSON в .NET. Начало
Допустим, у вас есть конечная точка, которая возвращает список: все заказы за последний квартал или поток показаний датчиков. Код выглядит нормально:
app.MapGet("/orders/export",
async (OrderService service) =>
{
List<Order> orders = await service.GetAllAsync();
return Results.Ok(orders);
});GetAllAsync извлекает все строки в List<Order>. Затем Results.Ok передаёт весь список сериализатору, который формирует JSON-массив в памяти. Т.е. вы храните две копии данных одновременно — объекты и их сериализованную форму, — а клиент ждёт, пока не будет сериализована последняя строка, прежде чем что-либо получить.Для пары сотен строк это не заметно. При паре десятков тысяч происходит скачок потребления памяти, начинает работать сборщик мусора, и время до получения первого байта растёт, как и нагрузка на процесс при увеличении количества одновременных запросов. Решение в том, чтобы прекратить буферизацию. Сериализуем элемент, отдаём, переходим к следующему.
System.Text.Json может передавать IAsyncEnumerable<T> потоком начиная с .NET 6. Платформа выдаёт JSON-массив потоком, а не буферизирует его:
// возвращает IAsyncEnumerable<Order>
app.MapGet("/orders/export", (OrderService service) =>
service.GetAllAsyncStream());
…
public async IAsyncEnumerable<Order>
GetAllAsyncStream(
[EnumeratorCancellation] CancellationToken ct = default)
{
await foreach (var order in _repo.ReadAllAsync(ct))
yield return order;
}
Это решает проблему использования памяти. Сервер хранит один заказ за раз, а не весь список. Вывод – JSON-массив:
[{"Id":1,"Total":42.0},{"Id":2,"Total":19.5}, …]Это вполне приемлемо для браузера, вызывающего функцию
fetch и использующего await res.json(). Но есть недостаток для больших объёмов данных: массив представляет собой один JSON-документ. Потребитель, желающий обрабатывать элементы по мере их поступления, должен разбирать массив постепенно, и, если соединение обрывается на полпути, остается усечённый, некорректный JSON-документ — завершающий символ ] не доходит. Добавить к нему данные тоже невозможно. Для потоковой обработки, записи в лог или экспорта данных JSON-массив имеет неправильный формат.Окончание следует…
Источник: https://thecodeman.net/posts/streaming-json-in-dotnet-with-json-lines
- 👍 6

