Архитектура Drupal под нагрузкой: что ломается первым
В Drupal контейнер сервисов редко становится первым узким местом. Под нагрузкой обычно раньше заканчиваются PHP-FPM-воркеры, растёт очередь к базе, а промахи кеша превращают плагины и рендеринг в дорогой путь.

На Drupal 10.x и 11.x сайт может начать отвечать 502 не из-за самого контейнера сервисов, а потому, что каждый промах кеша запускает цепочку обращений к базе, рендерингу и плагинам. Первым обычно заканчивается один из ограниченных ресурсов: PHP-FPM-воркеры, соединения с PostgreSQL или MySQL, память процесса либо время ответа внешней системы. Контейнер, сервисы и плагины важны потому, что определяют длину этой цепочки и цену одного запроса.
Что именно происходит с запросом
В Drupal контейнер сервисов собирает определения объектов: логгеров, кешей, менеджеров конфигурации, роутера, базы данных и других компонентов. В рабочем режиме контейнер не должен полностью пересобираться на каждый HTTP-запрос. Скомпилированные определения кешируются, а конкретные сервисы создаются по мере обращения к ним.
Это важное различие. Наличие большого количества сервисов в модуле само по себе не означает, что каждый запрос создаёт все эти объекты. Проблема появляется, когда код слишком рано получает тяжёлые зависимости, обходит кеш или запускает операцию, которая требует базы данных и внешних вызовов.
Плагины работают поверх другой подсистемы. Drupal ищет их определения, читает аннотации или атрибуты в зависимости от конкретного типа плагина и версии API, строит менеджер плагинов и кеширует результат. При обычном запросе повторное сканирование файлов не должно происходить постоянно. После очистки кешей, деплоя или изменения кода этот путь становится заметным.
Под нагрузкой ломается не самая сложная подсистема. Ломается самый короткий ограниченный ресурс на пути запроса.
Контейнер сервисов — объект, который знает, как создавать и связывать сервисы приложения. Сервисом может быть, например, клиент базы данных или менеджер кеша; контейнер не является самим кешем и не ускоряет запрос автоматически.
Что обычно заканчивается первым
1. PHP-FPM-воркеры
Если запрос выполняется дольше, чем ожидалось, каждый занятый PHP-FPM-воркер дольше не принимает новую работу. При очереди пользователи сначала видят рост времени ответа, затем таймаут прокси, а после — 502 или 504 в зависимости от того, какой компонент не дождался ответа.
Причина часто находится не в Drupal: медленный SQL, внешний API, генерация большого представления или блокировка в базе удерживают воркер. Увеличить число процессов можно, но если память сервера ограничена, вы получите swap или аварийное завершение процессов вместо пропускной способности.
2. База данных и соединения
Промах по кешу страницы может повлечь загрузку сущностей, полей, прав доступа, меню и конфигурации. Один пользовательский запрос тогда превращается в множество SQL-операций. Под параллельной нагрузкой растут количество запросов, время ожидания блокировок и очередь соединений.
Особенно дорогими становятся списки с фильтрами, сортировкой и пагинацией по большим таблицам, а также страницы, на которых один и тот же набор сущностей загружается несколькими сервисами. Сам контейнер здесь лишь доставляет менеджеры и репозитории. Узкое место находится ниже — в запросах, индексах, блокировках и размере результата.
3. Кеши после очистки
После очистки кешей несколько первых запросов начинают заново строить маршруты, определения плагинов, конфигурацию и результаты рендеринга. Если одновременно приходит много трафика, каждый процесс может пытаться вычислить один и тот же результат. Это называют холодным стартом кеша.
Риск выше, если деплой автоматически очищает все кеши в рабочее время. Однократная операция обслуживания превращается в короткий, но резкий всплеск нагрузки. Для крупного сайта важны не только средние значения, но и p95 или p99 времени ответа сразу после релиза.
4. Внешние сервисы
Сервис, который синхронно обращается к платёжному шлюзу, поиску, CRM или файловому хранилищу, связывает время ответа Drupal с чужой системой. Десять секунд ожидания внешнего HTTP-запроса — это не десять секунд для одного абстрактного процесса: всё это время занят PHP-FPM-воркер.
Нужны таймауты, ограничение повторных попыток и понятная деградация. Без них сбой внешнего сервиса быстро превращается в очередь внутри вашего приложения. Настройка контейнера не исправит зависимость, которая не умеет завершаться вовремя.
Как связаны контейнер, сервисы и плагины
Сервисный слой: дешёвое получение, дорогая работа
Получить сервис из контейнера обычно не самая затратная операция. Дороже то, что сервис делает в конструкторе или при первом вызове: читает конфигурацию, открывает соединение, загружает схему, обращается к файловой системе или строит большой объект.
Поэтому на ревью опаснее не само количество аргументов конструктора, а побочные эффекты. Конструктор сервиса должен подготавливать зависимости, а не выполнять сетевой запрос или массово загружать сущности. Иначе даже без фактического использования функциональности запрос платит за её инициализацию.
Плагины: стоимость скрыта в discovery и исполнении
Система плагинов даёт расширяемость, но добавляет два разных расхода. Первый — обнаружение и построение определений после очистки или обновления кешей. Второй — выполнение выбранных плагинов в рабочем запросе.
Если модуль перебирает все плагины, а затем для каждого читает конфигурацию или делает запрос к базе, стоимость растёт линейно с количеством плагинов. Если внутри цикла есть загрузка сущностей, рост может оказаться ещё хуже из-за повторных запросов.
Не следует путать кеш менеджера плагинов с кешем бизнес-результата. Первый помогает не искать определения заново. Он не делает быстрым вызов каждого плагина и не заменяет кеширование результата операции.
События и хуки увеличивают поверхность запроса
Событийный подход удобен: модуль подключается к нужному этапу, не меняя основной код. Но один HTTP-запрос может пройти через множество обработчиков. Сложность трудно заметить, если каждый обработчик занимает по несколько миллисекунд, а проблема возникает только на странице с большим числом сущностей.
На ревью ищите обработчики, которые запускаются для каждого элемента списка, каждого рендера или каждой операции сохранения. Запрос к базе внутри такого обработчика — кандидат на немедленное измерение. Отдельно проверьте рекурсивные цепочки, когда изменение сущности вызывает новый обработчик сохранения.
Сравнение узких мест
| Симптом | Вероятный ресурс | Что измерять | Первый шаг |
|---|---|---|---|
| Растёт очередь, появляются 502 или 504 | PHP-FPM или медленная зависимость | Занятые процессы, очередь, p95/p99, время upstream | Найти самые долгие запросы и внешние вызовы |
| Высокая загрузка базы, запросы ждут | SQL, блокировки, соединения | Время запросов, активные соединения, блокировки, планы | Сопоставить медленные SQL с URL и операциями Drupal |
| Всплеск после очистки кеша | Холодные кеши и параллельный прогрев | Ошибки кеша, latency после деплоя, нагрузку на БД | Разнести очистку и прогрев, сравнить с тёплым запуском |
| Долго после включения модуля | Discovery плагинов или новый обработчик | Время rebuild, число плагинов, профилирование запроса | Проверить изменения модуля и путь исполнения |
| Зависание на отдельных страницах | Внешний сервис или тяжёлый список | Таймауты, SQL, размер результата, количество сущностей | Добавить границы времени и измерить страницу по этапам |
Где подход ломается
«Добавим больше PHP-процессов»
Это помогает только пока хватает CPU, памяти и downstream-ресурсов. Если каждый процесс ждёт базу, увеличение пула создаёт больше параллельных запросов к той же базе. В результате средняя загрузка PHP может выглядеть приемлемо, а база и память уже работают на пределе.
«Очистим все кеши после каждого изменения»
Для разработки это привычно. Для production-окружения такая операция требует окна, контроля трафика и проверки прогрева. Иначе релиз совпадёт с холодным стартом, а пользователи станут тестом производительности.
«Вынесем тяжёлую логику в сервис»
Сервис не становится дешёвым только потому, что его назвали сервисом. Если он вызывается в рендере каждой строки и внутри загружает сущности по одной, архитектурное имя не меняет алгоритм. Групповая загрузка, корректный кеш и перенос фоновой работы часто важнее самого факта внедрения зависимости.
«Проверим только среднее время ответа»
Среднее скрывает редкие, но болезненные запросы. Смотрите распределение времени, долю ошибок, количество запросов к базе на URL и состояние очереди PHP-FPM. Для сравнения до и после фиксируйте версию Drupal, включённые модули, размер базы, тип кеша и одинаковый сценарий нагрузки.
Как разбирать инцидент без догадок
- Зафиксируйте симптом. Запишите URL, время начала, код ответа, p50, p95 и p99, число одновременных запросов и момент последнего деплоя. Так вы отличите общий перегруз от одной проблемной страницы.
- Проверьте очередь PHP-FPM. Сопоставьте занятые процессы с лимитом пула и временем выполнения. Если очередь растёт, ищите, чем заняты воркеры, а не начинайте с контейнера.
- Сопоставьте запрос с базой. Найдите SQL, которые выполняются дольше всего, повторяются в одном HTTP-запросе или появляются сотнями. Затем проверьте планы и блокировки в самой СУБД.
- Сравните тёплый и холодный кеш. Повторите один сценарий после очистки кешей и после прогрева. Разница покажет, связана ли проблема с discovery, сборкой контейнера или обычным путём исполнения.
- Проверьте изменения модулей. Ищите новые плагины, обработчики событий, запросы в циклах и синхронные внешние вызовы. Откат на тестовом окружении должен менять один фактор за раз.
Профилирование нужно включать на тестовом или ограниченном окружении, а не бездумно на всём production-трафике. Трассировка должна связывать URL, PHP-исполнение, SQL, кеш и внешний запрос в одну цепочку.
Что измерять после исправления
Сравнивайте не только время страницы, но и цену запроса: число SQL-операций, суммарное время базы, количество обращений к кешу, долю промахов, время внешних зависимостей и потребление памяти PHP-процесса. Для страниц со списками отдельно фиксируйте размер выборки и число отображаемых элементов.
После релиза наблюдайте первые минуты: latency после очистки кеша, ошибки 502/504, очередь PHP-FPM и нагрузку на базу. Если сервис стал быстрее в тёплом кеше, но проваливается после каждого деплоя, проблема не решена — вы измерили только благоприятный сценарий.
Что делать дальше
Составьте для одного критичного URL карту пути: вход в Drupal, контроллер, сервисы, плагины, SQL, кеши и внешние вызовы. Затем снимите два профиля на Drupal 10.x или 11.x в вашей точной минорной версии: с тёплым кешем и сразу после его очистки. Следующим шагом настройте алерты на p95/p99, очередь PHP-FPM, медленные SQL и ошибки внешних зависимостей — именно эти показатели покажут, что ломается первым после следующего изменения.
Архитектор и дизайнер интерьера: Законные перепланировки (БТИ), расчёт плитки, обоев, материалов.
Спросить за 15 ₽Источники: Drupal.org: Services and dependency injection, Drupal.org: Caching overview
Частые вопросы
Контейнер сервисов пересобирается на каждый запрос Drupal?
Почему после очистки кеша сайт резко замедляется?
Поможет ли увеличение числа PHP-FPM-воркеров?
Как понять, что проблема именно в плагинах?
Что делать, если после исправления среднее время улучшилось, но 502 остались?
Изменится ли поведение между Drupal 10 и Drupal 11?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.