Разработчики наблюдают, как их только что запущенное приложение начинает тормозить, как только несколько тысяч пользователей нажимают кнопку «пуск». И это замедление редко связано с ошибкой в коде — чаще всего это борьба процессора и оперативной памяти сервера за ресурсы. Узкое место проявляется в виде долгой загрузки страниц, таймаутов или полных сбоев, что негативно сказывается на пользовательском опыте, выручке и доверии к бренду.

Почему сервер, который отлично работал в лабораторных условиях, может полностью остановиться в продакшене

Во время разработки один разработчик отправляет лишь несколько запросов, поэтому ресурсы сервера большую часть времени простаивают. Когда приложение выходит в свет, каждый посетитель генерирует запрос, которому необходимы два основных компонента:

  • CPU (центральный процессор) — процессор, который выполняет каждый цикл, функцию и вычисление. Представьте, что это повар, который может готовить лишь ограниченное количество блюд одновременно. Один заказ отдается мгновенно; сто заказов означают, что повар работает с той же скоростью, но посетителям приходится ждать дольше.
  • RAM (оперативная память) — временное хранилище данных, которые необходимы процессору при обработке запроса. Это похоже на рабочий стол, на котором повар держит ингредиенты для каждого блюда. Если стол завален, повар должен перестать принимать новые заказы, пока место не освободится.

Когда тысячи пользователей входят в систему одновременно, каждый запрос требует своей доли процессорного времени и своего объема оперативной памяти. Ограниченный пул обоих ресурсов сервера распределяется между запросами, и очередь растет. Сам сервер не стал работать медленнее; увеличилось время ожидания для каждого отдельного запроса.

Искушение «просто купить коробку побольше»

Обычная первая реакция — обновить машину, что называется вертикальным масштабированием. Увеличение количества ядер CPU или объема RAM действительно повышает мощность: переход с 4 ядер на 16 или с 8 ГБ на 64 ГБ позволяет справиться с более резким скачком трафика без изменения кода.

Однако у вертикального масштабирования есть жесткий потолок:

  • Физические ограничения — любая материнская плата может вместить лишь определенное количество ядер и ограниченный объем памяти.
  • Убывающая отдача — каждое дополнительное ядро или гигабайт стоят дороже предыдущих, в то время как прирост производительности уменьшается.
  • Единая точка отказа — если этот сверхмощный сервер выйдет из строя, весь сервис перестанет работать.

Из-за этих ограничений тяжеловесы индустрии — стриминговые платформы, поисковые системы, сайты электронной коммерции — отказались от использования одной «машины-монстра».

Альтернатива: распределение нагрузки между множеством серверов поменьше

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

Для координации множества машин требуется балансировщик нагрузки (load balancer) — программное или аппаратное обеспечение, которое принимает каждый входящий запрос и перенаправляет его на сервер с наибольшим количеством свободных ресурсов. Балансировщик скрывает сложность от клиента; с точки зрения пользователя сайт по-прежнему выглядит как единая конечная точка.

Горизонтальное масштабирование также обеспечивает отказоустойчивость. Если один узел выходит из строя, балансировщик просто перенаправляет трафик на остальные исправные узлы, поддерживая работу сервиса.

На что стоит обратить внимание при добавлении новых машин

  • Архитектура без сохранения состояния (stateless design) — запросы не должны зависеть от данных, хранящихся только в памяти конкретного сервера; иначе пользователя может перебросить на узел, у которого нет необходимого контекста. Решить эту проблему помогают общие кэши или базы данных.
  • Проверки состояния (health checks) — балансировщик должен уметь быстро обнаруживать неисправный сервер и прекращать отправку на него трафика.
  • Политики автомасштабирования (auto-scaling policies) — многие облачные платформы позволяют задавать пороги (загрузка CPU, задержка запроса), при которых система автоматически запускает или отключает экземпляры, удерживая затраты в соответствии со спросом.

Контраргумент: вертикальное масштабирование еще живо

Для небольших команд или приложений с низким трафиком один мощный сервер может стать самым простым и дешевым решением. Если всплеск трафика предсказуем (например, запланированный запуск продукта), временное вертикальное обновление может быть более практичным, чем развертывание целого флота новых экземпляров.

Главное — вовремя понять, когда трюк с «коробкой побольше» перестает приносить пропорциональную выгоду, и начать планировать распределение нагрузки.

Итог

Замедление работы сервера после запуска обычно вызвано конкуренцией за ресурсы, а не дефектом в коде. Циклы процессора и объем оперативной памяти ограничены, и когда одновременно поступает множество запросов, они выстраиваются в очередь, что увеличивает время отклика. Вертикальное масштабирование дает некоторый запас производительности, но вскоре упирается в физические и экономические пределы. Горизонтальное масштабирование — добавление менее мощных серверов за балансировщиком нагрузки — предлагает более дешевый и отказоустойчивый путь по мере роста трафика. Как только вы заметите рост очереди, придет время оценить, хватит ли нескольких дополнительных ядер или стоит начать распределять нагрузку между множеством машин.

Источник: статья на dev.to «Why Servers Slow Down – CPU, RAM and the hidden cost of every request».