Балансировка: round-robin, weight и least_conn
Балансировка нагрузки — это искусство равномерно раздать работу нескольким бэкендам, чтобы ни один не задохнулся, пока другой скучает.
«Round-robin честно раздаёт по очереди. least_conn умнее: отдаёт тому, кто меньше всех занят прямо сейчас.»
Когда в upstream несколько серверов, Nginx распределяет между ними запросы. Это и есть балансировка нагрузки. Метод задаётся внутри блока upstream; по умолчанию — round-robin.
Round-robin: по кругу
upstream backend {
server 10.0.0.1;
server 10.0.0.2;
server 10.0.0.3;
}
# запросы идут по очереди: 1, 2, 3, 1, 2, 3, ...
запрос 1 -> сервер A запрос 2 -> сервер B запрос 3 -> сервер C запрос 4 -> сервер A (круг замкнулся)
Веса серверов
upstream backend {
server 10.0.0.1 weight=3; # получит втрое больше запросов
server 10.0.0.2 weight=1;
}
Веса полезны, когда серверы разной мощности: мощному даём больший вес.
least_conn: наименее загруженному
upstream backend {
least_conn;
server 10.0.0.1;
server 10.0.0.2;
}
При least_conn очередной запрос уходит серверу с наименьшим числом активных соединений. Это спасает, когда запросы разной длительности: round-robin может свалить тяжёлый отчёт на и без того занятый сервер, а least_conn выберет свободный.
Смоделируем round-robin с весами на Python
servers = [("A", 3), ("B", 1), ("C", 1)] # (имя, вес)
# Разворачиваем веса в плоский список слотов
slots = []
for name, w in servers:
slots.extend([name] * w)
print("Слоты round-robin:", slots)
idx = 0
for request in range(1, 11):
chosen = slots[idx % len(slots)]
print(f"запрос {request:2} -> сервер {chosen}")
idx += 1
Попробуй сам ▶ Видно, что сервер A с весом 3 получает примерно втрое больше запросов, чем B и C. Реальный Nginx использует «сглаженный» взвешенный round-robin, чтобы не отдавать A три раза подряд, но пропорция та же.
Как работает под капотом
Round-robin в Nginx — это сглаженный взвешенный алгоритм: он не просто крутит список, а на каждом шаге выбирает сервер с наибольшим «текущим весом», затем уменьшает его. Так запросы к серверу с весом 3 распределяются равномерно во времени, а не пачкой. least_conn же на каждом запросе смотрит счётчики активных соединений и берёт минимальный (с поправкой на веса).
Частые ошибки
- round-robin при сильно разной длительности запросов. Долгие запросы накапливаются на «невезучих» серверах; используй least_conn.
- Равные веса на неравном железе. Слабый сервер захлёбывается, мощный простаивает — расставь веса.
- Думать, что least_conn учитывает загрузку CPU. Он считает соединения, а не процессор; это приближение, а не точная метрика.
Best practices
- Stateless-сервисы с похожими по времени запросами — round-robin.
- Запросы разной длительности (отчёты, загрузки) —
least_conn. - Разнородное железо — расставь
weightпропорционально мощности.
Когда балансировки на Nginx достаточно, а когда нет
Nginx как балансировщик 7-го уровня (он понимает HTTP) прекрасно подходит для типичной задачи: распределить веб-трафик между несколькими копиями приложения на одной или нескольких машинах. Для подавляющего большинства проектов этого достаточно, и городить отдельный балансировщик не нужно. Но полезно понимать границы. Если нагрузка такова, что сам Nginx становится узким местом или единой точкой отказа, перед ним ставят балансировку 4-го уровня (по TCP/IP) — например, облачный L4-балансировщик распределяет трафик уже между несколькими Nginx, а каждый из них балансирует на бэкенды. Получается двухуровневая схема.
Выбор метода тоже зависит от природы трафика, и здесь нет «лучшего» варианта на все случаи. Round-robin идеален, когда запросы примерно одинаковы по стоимости — типичный REST API с короткими ответами. least_conn выигрывает, когда время обработки скачет: одни запросы отдаются мгновенно, другие генерируют тяжёлый отчёт минуту. Тогда round-robin может свалить новый тяжёлый запрос на сервер, который и так молотит долгий, а least_conn отдаст его свободному. На практике least_conn — разумный выбор по умолчанию для неоднородной нагрузки, и многие команды ставят именно его, не задумываясь. Веса же подключают, когда машины в пуле объективно разной мощности — старый сервер и новый рядом. Понимание этих компромиссов позволяет настроить балансировку под реальный профиль нагрузки, а не наугад.
Итоги
Round-robin (по умолчанию) раздаёт запросы по кругу, веса корректируют доли под мощность серверов, а least_conn отдаёт запрос наименее занятому — это лучше при разной длительности обработки. Дальше — sticky-сессии через ip_hash и проверки здоровья бэкендов.