Балансировка nginx: как работает smooth weighted round-robin и почему effective_weight ломает распределение
Когда вы настраиваете в nginx upstream с весами, например server a.internal weight=10 maxfails=2, вы ожидаете, что трафик распределится строго в пропорции 10:1. Но реальность часто оказывается иной. Ночью один из бэкендов на пару секунд отвалился, к утру он давно ожил и не выведен из ротации, а перв

Когда вы настраиваете в nginx upstream с весами, например server a.internal weight=10 maxfails=2, вы ожидаете, что трафик распределится строго в пропорции 10:1. Но реальность часто оказывается иной. Ночью один из бэкендов на пару секунд отвалился, к утру он давно ожил и не выведен из ротации, а первые запросы рабочего дня уходят совсем не туда, куда вы планировали. Логи молчат, документация по upstream не даёт ответа. Разгадка кроется в двух строках исходного кода ngxhttpupstreamroundrobin.c. Разберём, как работает smooth weighted round-robin, что такое effectiveweight и почему leastconn сравнивает не число соединений.
Как работает weighted round-robin в nginx
Классический weighted round-robin, описанный в документации, — это простой алгоритм: каждый сервер получает количество запросов, пропорциональное его весу. Например, при весах 10 и 1 сервер A получает 10 запросов подряд, затем сервер B — один, и так по кругу. Но nginx использует не классический, а сглаженный (smooth) weighted round-robin, который был предложен в 2016 году как улучшение. Его цель — избежать «комков» запросов на одном сервере и распределить нагрузку более равномерно в каждый момент времени.
Алгоритм работает так: у каждого сервера есть текущий вес (currentweight), который изначально равен нулю. При каждом новом запросе nginx увеличивает currentweight на weight сервера, затем выбирает сервер с максимальным currentweight, отдаёт ему запрос, после чего уменьшает его currentweight на сумму всех весов. Этот цикл повторяется. На первый взгляд всё логично, но есть нюанс: в nginx также существует параметр effectiveweight, который не описан в официальной документации. И именно он управляет распределением в случае сбоев.
Что такое effectiveweight и как maxfails влияет на распределение
effectiveweight — это внутренняя переменная, которая изначально равна weight сервера. Когда сервер неудачно отвечает на запрос (например, превышен maxfails или произошла ошибка соединения), nginx уменьшает effectiveweight на единицу. Если сервер успешно обрабатывает запросы, effectiveweight постепенно восстанавливается до исходного значения. Этот механизм позволяет алгоритму временно снижать долю трафика на нестабильных серверах, не выводя их полностью из ротации.
Проблема в том, что effectiveweight не участвует в формуле выбора сервера напрямую — он влияет на currentweight только при обновлении. Когда сервер возвращается в строй после сбоя, его currentweight может быть значительно ниже, чем у остальных, поэтому первые запросы уходят не ему, а соседям. Именно это вы наблюдаете утром: сервер A с весом 10 и сервер B с весом 1, но после ночного сбоя у A effectiveweight снижен, и распределение становится почти равным, пока effectiveweight не восстановится. Всё это происходит незаметно для логов, потому что nginx не пишет о временных изменениях effectiveweight.
Почему после сбоя сервер получает меньше запросов, чем ожидалось?
Когда сервер временно недоступен, nginx уменьшает его effectiveweight. Это приводит к тому, что после восстановления сервер получает меньшую долю трафика, пока effectiveweight не вернётся к исходному значению. Время восстановления зависит от количества успешных запросов, поэтому в периоды низкой нагрузки (например, ночью) сервер может оставаться «ослабленным» в течение длительного времени. Утром, когда трафик резко возрастает, распределение всё ещё смещено, и это может вызвать перегрузку других серверов.
Почему leastconn сравнивает не число соединений
Директива leastconn выбирает сервер с наименьшим числом активных соединений, но на самом деле сравнение идёт не по абсолютному числу, а по взвешенному значению. Для каждого сервера вычисляется currentweight (как в round-robin), а затем выбирается тот, у которого это значение минимально. Это сделано для того, чтобы учитывать вес сервера: если один сервер имеет вес 10, а другой — 1, то при равном числе соединений предпочтение отдаётся более мощному. Однако если вы не используете веса, leastconn работает как обычный least-connections.
Почему iphash не работает на unix-сокетах
Директива iphash привязывает клиента к серверу на основе хэша его IP-адреса. Но есть ограничение: если в upstream используются unix-сокеты (например, server unix:/var/run/backend.sock), то iphash не работает. Причина в том, что для unix-сокетов нет IP-адреса, и nginx не может вычислить хэш. Вместо этого он использует обычный round-robin, что может привести к нарушению липкости сессий. Это важно учитывать при настройке.
Sticky и leasttime: новые алгоритмы в open source
Несколько месяцев назад в open source версию nginx приехали новые алгоритмы балансировки: sticky и leasttime. sticky позволяет привязывать запросы клиента к одному и тому же серверу на основе cookie или других параметров, что критично для приложений с сессиями в памяти. leasttime выбирает сервер с наименьшим временем ответа, что может быть полезно для оптимизации задержек. Эти алгоритмы уже давно были в коммерческой версии nginx Plus, и теперь доступны всем.
Кого затронет и как
Эта информация критична для DevOps-инженеров, администраторов и всех, кто настраивает балансировку в nginx. Если вы используете weighted round-robin и замечаете неравномерное распределение трафика после сбоев, теперь вы знаете причину. Для бизнеса это может означать неожиданные перегрузки на одних серверах и недоиспользование других, что влияет на стабильность и стоимость инфраструктуры. Разработчикам стоит учитывать особенности iphash при работе с unix-сокетами, а также изучить новые алгоритмы для улучшения пользовательского опыта.
Что будет дальше
Скорее всего, nginx продолжит развивать алгоритмы балансировки, добавляя новые возможности из коммерческой версии в open source. Следите за обновлениями и тестируйте новые директивы на своих стендах. Возможно, в следующих версиях появится документация по effectiveweight и другим внутренним механизмам, но пока полагаться приходится на исходный код и сообщество.
Итог
Понимание внутренностей nginx — ключ к эффективной настройке балансировки. Теперь вы знаете, что weighted round-robin — это не просто «десять к одному», а сложный алгоритм с недокументированными параметрами, которые могут влиять на распределение трафика. Следите за новыми алгоритмами и не забывайте про ограничения, такие как iphash на unix-сокетах. Это поможет вам избежать сюрпризов в продакшене и обеспечить стабильную работу ваших сервисов.