TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
.NET Разработчик

8 Sep, 08:01

Open in Telegram Share Report

День 2778. #ЗаметкиНаПолях
Потоковая Передача JSON в .NET. Начало
Допустим, у вас есть конечная точка, которая возвращает список: все заказы за последний квартал или поток показаний датчиков. Код выглядит нормально:
app.MapGet("/orders/export",
async (OrderService service) =>
{
List orders = await service.GetAllAsync();
return Results.Ok(orders);
});
GetAllAsync извлекает все строки в List. Затем Results.Ok передаёт весь список сериализатору, который формирует JSON-массив в памяти. Т.е. вы храните две копии данных одновременно — объекты и их сериализованную форму, — а клиент ждёт, пока не будет сериализована последняя строка, прежде чем что-либо получить.

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

System.Text.Json может передавать IAsyncEnumerable потоком начиная с .NET 6. Платформа выдаёт JSON-массив потоком, а не буферизирует его:
// возвращает IAsyncEnumerable
app.MapGet("/orders/export", (OrderService service) =>
service.GetAllAsyncStream());

…

public async IAsyncEnumerable
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

1.6k 2 23 6
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot