Алгоритм балансировки в HAProxy — это не галочка в конфиге, а фундаментальная часть архитектуры. От него зависят равномерность нагрузки, «липкость» сессий, предсказуемость поведения при отказах/масштабировании и итоговая латентность. В этой статье я разберу пять популярных режимов: roundrobin, leastconn, source (source hash), uri и maglev. Дам практические рекомендации, покажу типовые паттерны и подводные камни, о которые чаще всего спотыкаются в проде.
Базовые принципы балансировки в HAProxy
HAProxy применяет алгоритм на уровне стрима (HTTP-запроса, TCP-соединения) в контексте конкретного backend. Выбор сервера — это функция от входного признака, состояния пула и самих правил. Важные факторы:
- Тип трафика: HTTP c keep-alive, HTTP/2, gRPC, WebSocket, TCP-прокси.
- Степень «липкости»: нужна ли привязка клиента/сессии к одному бэкенду.
- Нагрузка запросов: однородная или «тяжёлые» и «лёгкие» запросы перемешаны.
- Динамика пула: как часто добавляются/убираются сервера, как ведёт себя алгоритм при изменениях.
- Ограничения серверов:
maxconn,minconn,slowstart, веса.
Конфигурация алгоритма не заменяет правильный лимит соединений на приложении, здравую политику таймаутов и чёткие health-check’и. Балансировщик не лечит узкие места на бэкенде — он помогает жить с ними.
roundrobin: предсказуемая цикличность
roundrobin — равномерная по очередям раздача запросов по кругу с учётом веса. Это хороший дефолт для однородных запросов и бэкендов одинаковой мощности. Поведение простое, диагностика прозрачная.
Где уместен
- Статические файлы, CDN-ориджин, лёгкие API c узким разбросом времени ответа.
- Когда важна простота и предсказуемость, нет требования к «липкости».
Подводные камни
- Не учитывает загруженность по соединениям: при keep-alive часть серверов может держать больше активных запросов.
- Разброс latency растёт на неоднородных нагрузках (тяжёлые/лёгкие запросы перемешаны).
Пример
backend app_rr
balance roundrobin
default-server maxconn 100 slowstart 5s
server app1 10.0.0.11:8080 check weight 2
server app2 10.0.0.12:8080 check weight 1
leastconn: минимум активных соединений
leastconn выбирает сервер с наименьшим числом активных соединений. На неоднородных нагрузках часто даёт более низкий P95/P99, чем roundrobin, потому что «обходит» перегруженные экземпляры.
Где уместен
- Долгие запросы (генерация отчётов, медленные БД-запросы, загрузка/выгрузка больших объектов).
- Неоднородная нагрузка по времени обработки.
Подводные камни
- «Активные соединения» — не «активные запросы». При HTTP keep-alive соединение может висеть без запросов, а при HTTP/2 в одном соединении мультиплексируются несколько запросов. Это искажает метрику.
- Нужна трезвая настройка
maxconnнаserverи таймаутов, иначе легко уткнуться в head-of-line blocking на приложении.
Пример
backend app_lc
balance leastconn
default-server maxconn 200 slowstart 10s
server app1 10.0.0.21:8080 check
server app2 10.0.0.22:8080 check

source (source hash): детерминизм по клиенту
balance source использует хеш источника (обычно IP клиента) и маршрутизирует запросы детерминированно на один и тот же бэкенд. Это даёт «липкость» без кук и без хранения состояния. Часто применяют для stateful-приложений с сессиями в памяти, а также для кешей, где важно попадание на «свой» шард.
Где уместен
- Приложения, которым нужна липкость сессии, но нет/нежелательно куки.
- Кеши/шардинг по клиенту для повышения кэш-хитов.
Подводные камни
- NAT и прокси: многие клиенты приходят с одного IP, нагрузка перекосится. Обязательно убедитесь, что HAProxy видит реальный адрес (PROXY protocol, корректные заголовки X-Forwarded-For и настройка
option forwardforпо ситуации). - Изменение пула серверов приводит к перераспределению. Чтобы снизить «дребезг», включают
hash-type consistentили используютmaglev.
Пример
backend app_source
hash-type consistent
balance source
server app1 10.0.0.31:8080 check
server app2 10.0.0.32:8080 check
server app3 10.0.0.33:8080 check
С hash-type consistent при добавлении/удалении сервера перераспределяется лишь небольшая часть клиентов (а не все сразу, как при классическом modulo-хеше).
uri: хеш по содержимому URL
balance uri хеширует часть пути и детерминированно отправляет одинаковые URL на один и тот же бэкенд. Это полезно для кешей и CDNs, а также для нагрузки с «тяжёлыми» и часто повторяющимися путями.
Где уместен
- Origin для CDN/кеша: одинаковые пути лучше попадут на один сервер и прогреют локальный кеш.
- API с доминирующими «горячими» эндпоинтами (например, отчёты или популярные ресурсы).
Тонкости конфигурации
whole: хешировать весь URI.len N: ограничить длину учитываемого фрагмента.depth N: учитывать только первые N сегментов пути.- Рекомендуется
hash-type consistentдля минимального дробления кэша при изменении пула.
Примеры
backend cdn_uri_whole
hash-type consistent
balance uri whole
server s1 10.0.0.41:8080 check
server s2 10.0.0.42:8080 check
backend api_uri_depth
hash-type consistent
balance uri depth 2
server a1 10.0.0.51:8080 check
server a2 10.0.0.52:8080 check
Помните, что включение query string в хеш может резко снизить попадание в кеш. Часто выгоднее нормализовать URL, убрать трекинговые параметры на уровне прокси и лишь потом хешировать.
maglev: минимальные перестановки при изменении пула
balance maglev — реализация алгоритма Maglev хеширования, ориентированного на равномерность и минимальные изменения маршрутизации при добавлении/удалении серверов. В отличие от классического ring- или modulo-хеширования, maglev даёт стабильную, хорошо распределённую таблицу с крайне малой турбулентностью.
Где уместен
- Автоскейлинг: постоянное появление/исчезновение серверов без «перемешивания» всего трафика.
- Stateful-сервисы без внешнего стореджа сессий, кеши, шардинг по ключу.
- Большие пулы, где важно минимизировать кэш-миссы при изменениях.
Подводные камни
- Ограниченная или отсутствующая поддержка весов в ряде версий. Не полагайтесь на точные веса — проверяйте поведение на своей версии HAProxy.
- Детерминизм чувствителен к исходному ключу. Если ключ «шумный» (например, случайные запросы), балансировка получится близкой к равномерной, но «липкость» в этом нет смысла.
Пример
backend shard_maglev
balance maglev
# hash-type не требуется, maglev сам обеспечивает минимальные перестановки
server n1 10.0.0.61:8080 check
server n2 10.0.0.62:8080 check
server n3 10.0.0.63:8080 check
Как выбрать алгоритм под задачу
Сводная логика выбора:
- Нагрузка однородна по времени обработки, липкость не нужна → roundrobin.
- Запросы сильно различаются по времени → leastconn с аккуратными лимитами
maxconnна каждыйserver. - Нужна липкость по клиенту без куки → source с hash-type consistent или maglev.
- Хочется детерминизма по пути/ресурсу → uri c hash-type consistent.
- Частые изменения пула, важна минимальная турбулентность → maglev.
Если приложение требует «липкости» сессии, самый управляемый способ — кука на стороне HAProxy или приложения. Хеш по
source/uri— это без-сессионная липкость, у неё свои ограничения.
Веса, slowstart, minconn/maxconn: как они влияют
Даже идеальный алгоритм испортят неверные лимиты. Несколько правил:
maxconnнаserverограничивает соединения к бэкенду. Это ваша «защита» от перегруза.minconnработает с динамическими весами, стараясь давать больше трафика сильным серверам, меньше — слабым.slowstartпомогает новым инстансам входить в строй плавно, чтобы кеш/джит/ВМ «прогрелись».- В связке с
leastconnчрезмерно высокийmaxconnможет ухудшить хвостовую латентность. - Для
maglevучитывайте ограниченность поддержки весов: обычно безопаснее держать одинаковые по мощности сервера.
Пример сбалансированных лимитов
backend app_balanced
balance leastconn
default-server maxconn 300 minconn 50 slowstart 15s
server app1 10.0.0.71:8080 check weight 100
server app2 10.0.0.72:8080 check weight 100
server app3 10.0.0.73:8080 check weight 50 # меньшая машина
HTTP/1.1, HTTP/2, gRPC: что меняется
В HTTP/1.1 с keep-alive каждый запрос — отдельное назначение, но соединения живут дольше, что влияет на leastconn. В HTTP/2 и gRPC мультиплексирование делает метрику «кол-во соединений» ещё менее показательной: в одном соединении может жить множество параллельных запросов. Практический вывод:
- Для H2/gRPC с неоднородными запросами просматривайте метрики не только по соединениям, но и по активным стримам на бэкенде (экспорт метрик из приложения или sidecar). Подробно о нюансах см. HAProxy и HTTP/2/gRPC.
- Чаще всего
roundrobinили детерминированные хеши (uri/maglev) дают более предсказуемый результат, чем «слепой»leastconnна H2.

Стабильность при отказах и масштабировании
Как ведут себя алгоритмы при изменениях пула:
- roundrobin/leastconn: не обещают детерминизма — после удаления/добавления сервера картина распределения меняется сразу.
- source/uri без
hash-type consistent: сильный «дребезг» — практически все ключи перераспределяются. - source/uri c
hash-type consistent: перераспределяется лишь часть ключей (минимально возможная для классического consistent hashing). - maglev: минимизирует перестановки ещё лучше, чем классический чёртов колечный хеш; особенно полезно для больших пулов и автоскейлинга на гибких ресурсах вроде облачных VDS.
Наблюдаемость и отладка
Чтобы подтвердить гипотезы по распределению, включите полезные логи и статистику:
- Логи: добавьте бэкенд/сервер,
src, путь, время обработки, ретраи. Смотрите процент попадания ключей на сервера. - Stats socket: команды вида
show stat,show servers stateпомогут увидеть активные/ожидающие соединения и лимиты. - Гистограммы латентности на стороне приложения: изменения в хвостах P95/P99 сразу покажут эффект от смены алгоритма.
- Для липкости и квот полезны stick-tables — разбор и практику смотрите в материале HAProxy stick-tables и rate limit.
Минимальный лог-формат для анализа
global
log-format "%ci %cp %ft %b %s %TR/%Tw/%Tc/%Tr/%Ta %ST %r"
Этого достаточно, чтобы начать видеть, куда попадает трафик и какова динамика времени ответа.
Частые ошибки
- Липкость по IP за NAT.
balance sourceбез учёта X-Forwarded-For приводит к перекосу, когда весь офис/оператор сидит за одним IP. - leastconn на H2/gRPC без метрик потоков. Количество соединений — слабый прокси-показатель загруженности.
- Смешение тяжёлых и лёгких запросов без очереди/лимитов. Алгоритм не спасёт от head-of-line на приложении, если нет
maxconnи грамотных таймаутов. - Ожидание «идеальных весов» в maglev. В ряде версий веса ограниченно поддерживаются или игнорируются — рассчитывайте на равные сервера или тестируйте отдельно.
- Отсутствие slowstart. Новый инстанс получает сразу полный трафик, что ломает кеш и JIT, ухудшает хвостовую латентность.
Комбинации и продвинутые приёмы
- Гибрид: sticky-cookie + roundrobin. Если приложение может работать с куками, дайте HAProxy управлять липкостью независимо от IP — меньше сюрпризов с NAT.
- uri для кеш-ориджина, leastconn для API. Разделите бэкенды по профилю нагрузки и дайте каждому свой алгоритм.
- maglev для шардинга, отдельный пул для фонов. Выделите тяжёлые задачи в другой
backend, чтобы они не мешали пользовательским запросам.
Практические шаблоны конфигураций
API с неоднородной нагрузкой
backend api_mix
balance leastconn
timeout connect 3s
timeout server 30s
default-server maxconn 250 slowstart 10s
server api1 10.0.1.11:8080 check
server api2 10.0.1.12:8080 check
Кеш-ориджин для CDN
backend origin_cache
hash-type consistent
balance uri depth 3
timeout connect 2s
timeout server 10s
default-server maxconn 400 slowstart 5s
server o1 10.0.2.11:8080 check
server o2 10.0.2.12:8080 check
server o3 10.0.2.13:8080 check
Stateful-сервис с автоскейлингом
backend stateful
balance maglev
timeout connect 2s
timeout server 20s
default-server maxconn 200 slowstart 10s
server st1 10.0.3.11:8080 check
server st2 10.0.3.12:8080 check
server st3 10.0.3.13:8080 check
Тестирование перед продом
Прежде чем менять алгоритм на бою, сделайте короткий тестовый план:
- Соберите профили нагрузки: распределение длительностей, доля keep-alive/H2, «горячие» URI.
- Выберите метрики успеха: P95/P99, кэш-хиты, количество ретраев, ошибки 5xx.
- Сымитируйте отказ и добавление сервера: смотрите «дребезг» распределения и рост латентности.
- Проверьте лимиты:
maxconn, таймауты,slowstart, health-check’и. - Сделайте поэтапный rollout (canary) с обратимым планом.
Выводы
Нет «лучшего» алгоритма балансировки на все случаи. roundrobin — отличная отправная точка для однородной нагрузки. leastconn снижает хвостовую латентность на разношёрстных запросах, но требует аккуратной настройки лимитов. Детерминированные хеши source и uri полезны для липкости и кешей, особенно в паре с hash-type consistent. Когда важны минимальные перестановки при изменении пула (автоскейлинг, большие кластера), maglev становится лучшим выбором. Подбирайте алгоритм под профиль нагрузки, держите под контролем лимиты и таймауты, валидируйте гипотезы метриками — и HAProxy отблагодарит вас стабильной, предсказуемой латентностью без неприятных сюрпризов.


