Как справиться с нагрузкой при большом количестве запросов на печать отчетов
Что проверяют
Умеете ли вы отделить приём запроса от тяжёлой работы и ограничить расход ресурсов, а не просто «добавить серверов». Следом обычно спрашивают, что будет с памятью на отчёте в миллион строк и как пользователь узнает о готовности.
Как отвечать
Генерация отчёта долгая и ест CPU и память, держать её в HTTP-потоке нельзя. У встроенного Tomcat в Spring Boot по умолчанию 200 рабочих потоков, и пачка тяжёлых отчётов их быстро съедает. Каркас:
- асинхронный приём:
POST /reportsсохраняет задание и возвращает202 Acceptedс идентификатором, клиент опрашивает статус или получает уведомление; - очередь заданий: RabbitMQ, Kafka или таблица в PostgreSQL, откуда воркеры забирают задания через
SELECT ... FOR UPDATE SKIP LOCKED. Доставка «хотя бы раз», поэтому обработка идемпотентна; - ограниченный пул:
ThreadPoolExecutorс потоками по числу ядер и ограниченной очередью, при переполнении ответ429или503. Это и есть обратное давление; - потоковая генерация: данные читаются курсором с
fetchSize, а не одним списком. Для Excel в Apache POI берутSXSSFWorkbook, он держит в памяти окно строк, а не весь файл; - дедупликация: хэш параметров как ключ, готовый файл лежит в объектном хранилище с TTL, повтор получает ссылку;
- изоляция: отчёты читают с реплики, у пользователей квоты через Bucket4j или Resilience4j.
Воркеры масштабируются по длине очереди. Метрики через Micrometer и Prometheus: глубина очереди, p95 времени генерации, занятость heap. Многократно упавшие задания уходят в очередь недоставленных.
Про виртуальные потоки Java 21 скажите аккуратно: они выручают на ожидании ввода-вывода, а генерация PDF упирается в процессор, и тысяча виртуальных потоков её не ускорит.
Где спотыкаются
Берут Executors.newFixedThreadPool, не зная, что внутри неограниченная LinkedBlockingQueue. Под пиком очередь растёт до OutOfMemoryError.
Грузят весь набор данных в память и строят отчёт через XSSFWorkbook. На больших выборках это падение, а не медленная работа.
Отвечают «поставим Kafka» и замолкают. Интервьюер ждёт следующего слоя: повторная доставка, предел параллельности, судьба упавших заданий.