Масштабирование и балансировка нагрузки
Когда Go-сервис перестаёт справляться с трафиком, есть два направления роста — дать ему машину побольше или поставить рядом ещё копий, — и почти весь интерес в том, что начинается после слова «ещё копий»: stateless-инстансы, балансировщик, который раскидывает запросы, реестр, который знает, кто из инстансов жив, и аккуратный вывод новой версии под трафик. Пока нагрузка небольшая, всё это кажется лишним; в масштабе именно здесь рождаются перекос нагрузки, отказы при failover и обвалы в проде.
Главная ловушка темы — считать, что масштабирование линейно и бесплатно: «мало мощности — добавил узел». Это не так, и об этом говорят законы масштабируемости. Закон Амдала ограничивает ускорение долей последовательного кода; USL добавляет штраф за координацию, и после некоторого числа узлов пропускная способность не растёт, а падает — узлы конкурируют и тратят время на согласование. Дальше всё решает геометрия: stateless-инстансы (а не липкие сессии), балансировщик, который учитывает мощность и латентность, реестр, выбрасывающий нездоровые инстансы, и разделение трафика, дающее откатить плохой релиз раньше, чем он положит сервис. Эта тема разбирает масштабирование по слоям — от выбора направления роста до канареечного вывода новой версии.
Карта темы
- Вертикальное и горизонтальное — рост машиной против роста копиями: вертикаль проста, но упирается в потолок и единую точку отказа, горизонталь снимает потолок ценой stateless-инстансов, балансировщика и согласованности.
- Законы масштабируемости — Амдал ограничивает ускорение последовательной долей, Густафсон сохраняет линейность при росте задачи, а USL добавляет штраф за кросс-токинг, после которого новый узел замедляет.
- Алгоритмы балансировки — round-robin, weighted, least-connections, выбор по латентности и хеширование по ключу, а также разница L4 и L7 балансировки.
- Привязка сессий — stateless-сессии через JWT или общий стор в Redis против липких сессий, которые перекашивают нагрузку и ломаются при failover.
- Обнаружение сервисов — реестр (Consul, etcd, Kubernetes DNS), который отображает имя сервиса в живые инстансы, с учётом health-check и разницей client-side и server-side discovery.
- Разделение трафика — постепенный вывод версии через canary, blue-green и процентные сплиты по фиче-флагам с откатом по метрикам.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
| Считать вертикаль конечной архитектурой | Упираешься в потолок самой большой машины и оставляешь единую точку отказа |
| Считать горизонталь бесплатной добавкой инстансов | Без stateless-инстансов второй под не находит сессию пользователя |
| Думать, что узлы дают линейное ускорение | Амдал и USL: после некоторого числа узлов координация съедает выигрыш |
| Игнорировать штраф USL за кросс-токинг | Новый узел не ускоряет, а замедляет — пропускная способность падает |
| Балансировать round-robin по разным по мощности машинам | Слабые инстансы перегружены, мощные простаивают — перекос нагрузки |
| Класть липкие сессии при состоянии в памяти инстанса | Нагрузка перекошена, а при падении пода сессия пользователя теряется |
| Держать состояние сессии в памяти процесса | Любой инстанс не взаимозаменяем — горизонталь и failover ломаются |
| Балансировать на статичный список инстансов | Запросы уходят на упавшие инстансы — нет health-aware discovery |
| Катить новую версию сразу на 100% трафика | Плохой релиз кладёт весь сервис без пути на быстрый откат |
Значение для собеседований
Масштабирование — обязательная тема senior-уровня Go-интервью в части system design, и проверяют не знание названий алгоритмов, а понимание того, что у роста есть цена. Интервьюер смотрит, отличаете ли вы вертикаль от горизонтали по обязательствам перед кодом, помните ли, что добавление узлов не линейно (Амдал и USL), выбираете ли балансировку под реальную топологию инстансов и держите ли сессии stateless, чтобы любой инстанс был взаимозаменяем.
Что обычно проверяют:
- В чём разница вертикального и горизонтального масштабирования и почему разумный порядок — сначала выжать вертикаль, потом уходить в горизонталь.
- Что говорят закон Амдала, закон Густафсона и Universal Scalability Law и почему после некоторого числа узлов пропускная способность падает.
- Какие есть алгоритмы балансировки (round-robin, weighted, least-connections, по латентности, хеширование) и чем L4 отличается от L7.
- Почему stateless-сессии предпочтительнее липких и чем липкие сессии вредят балансу и failover.
- Как устроено обнаружение сервисов через реестр с health-check и в чём разница client-side и server-side discovery.
- Как безопасно выводить новую версию через canary, blue-green и процентные сплиты с откатом по метрикам.
Типичный неверный ответ: «масштабирование — это просто добавить инстансов за балансировщиком». Это запускает разбор того, что без stateless-инстансов второй под не найдёт сессию, что round-robin по разным по мощности машинам перекашивает нагрузку, что закон Амдала и USL ограничивают выигрыш от новых узлов вплоть до его обнуления, и что катить релиз сразу на весь трафик — это отказ от единственного дешёвого способа откатиться.