Как уменьшить нагрузку на серверы при пакетной обработке звонков
Что проверяют
Умеете ли вы отличить «распределить нагрузку» от «уменьшить её» и знаете ли инструменты .NET для управляемой пакетной обработки. Типичный уточняющий вопрос: что будет, если звонки приходят быстрее, чем вы успеваете их обработать.
Как отвечать
Сначала уточните, что за пакет: записи о звонках (CDR) из АТС, файлы разговоров на транскрибацию, выгрузка для биллинга. От этого зависит, где узкое место: база, CPU или внешний API. Дальше каркас:
- ограниченная очередь и обратное давление: между приёмом и обработкой
System.Threading.ChannelsсChannel.CreateBoundedи режимомBoundedChannelFullMode.Wait. Производитель ждёт, вместо того чтобы раздувать память. Между сервисами ту же роль играет брокер, в RabbitMQ число неподтверждённых сообщений на потребителя ограничивает prefetch; - контроль параллелизма:
Parallel.ForEachAsyncсMaxDegreeOfParallelismилиSemaphoreSlim, чтобы не открыть сотню соединений к базе разом. Для внешних API естьSystem.Threading.RateLimiting; - пакетная запись: не
SaveChangesна каждую строку, аSqlBulkCopy,COPYв PostgreSQL илиExecuteUpdateAsyncв EF Core. Размер пакета подбирается замером, обычно сотни или тысячи строк; - потоковое чтение:
IAsyncEnumerableиAsNoTrackingвместо загрузки всего набора в список; - время запуска: тяжёлые пересчёты в
BackgroundServiceили через Hangfire и Quartz.NET в часы низкой нагрузки; - идемпотентность: у каждой записи свой ключ, повторная доставка не создаёт дубль, а упавший пакет можно перезапустить.
Закончите измерением: dotnet-counters, метрики OpenTelemetry, глубина очереди и время обработки пакета до и после изменений.
Где спотыкаются
Говорят «сделаем всё асинхронным». async/await освобождает потоки на время ввода-вывода, но CPU-работу не ускоряет, а без лимита параллелизма только быстрее перегружает базу.
Называют балансировщик способом снизить нагрузку. Он делит её между серверами, но суммарная работа не уменьшается, а общая база остаётся одной.
Обрабатывают пакет одной транзакцией целиком. Длинная транзакция держит блокировки и раздувает журнал, а ошибка в последней записи откатывает всю работу.