Суть Читайте реальные конфиги LB, сниппет балансировки, заголовок PROXY protocol и обработчик graceful shutdown — затем выбирайте поведение или исправление с наибольшим рычагом.
Высота — путь к senior
НольJuniorMiddleSenior
Ты на senior-высоте — в орбите
◷ 14 min
Поведение LB решается в конфигах и обработчиках завершения, а не на диаграммах. Прочитайте каждый сниппет, предскажите его поведение под нагрузкой и выберите исправление, которое senior-инженер сделает первым.
Цель
Отработайте цикл, который вы запускаете на каждом инциденте с LB: прочитать конфиг upstream или обработчик, предсказать режим отказа и взяться за изменение, которое действительно его чинит.
Сниппет 1 — блок upstream в nginx
upstream api { server 10.0.0.1:8080; server 10.0.0.2:8080; server 10.0.0.3:8080;}server { location / { proxy_pass http://api; }}
Викторина
Completed
Стоимость запросов сильно разнится, и один backend периодически уходит в паузу GC. С этим default-конфигом что произойдёт и какое однострочное изменение даёт наибольший рычаг?
Heads-up Default в nginx — round-robin, а не least_conn. Без директивы он слеп к нагрузке и продолжает кормить паузящий backend.
Heads-up Блок валиден; отсутствие директивы просто выбирает default round-robin. Проблема в том, что default слеп к нагрузке, а не в синтаксической ошибке.
Heads-up Добавление ёмкости не мешает round-robin маршрутизировать в паузящий узел. Исправление — это load-aware политика, а не сырая ёмкость.
Сниппет 2 — power-of-two-choices вручную
import randomdef pick_backend(backends): a, b = random.sample(backends, 2) # два разных, равномерно случайно return a if a.active_conns <= b.active_conns else b
Викторина
Completed
Это ядро power-of-two-choices. Почему выборка ровно двух — а не скан всех N ради истинного минимума — важна в продакшене?
Heads-up Настоящий least-connections сканирует все N ради глобального минимума, что O(N) и синхронизирует всех клиентов на один восстановившийся узел. Выборка двух ломает и то и другое.
Heads-up Выборка двух не просто дешевле — она даёт дисбаланс ~12% против ~100% у чистого random и рассинхронизирует стадо, чего скан всех N не делает.
Heads-up Выбираются два РАЗНЫХ backend без возвращения; сравнение backend с самим собой было бы пустой операцией и сводило бы на нет шаг сравнения.
Backend на чистом TCP (не HTTP) нуждается в реальном IP клиента для rate limiting. LB добавляет строку выше. Какое утверждение верно?
Heads-up X-Forwarded-For — это HTTP-заголовок (уровень приложения); PROXY protocol — это преамбула транспортного уровня, работающая даже для не-HTTP протоколов вроде PostgreSQL или MQTT.
Heads-up Формат: `PROXY <proto> <src> <dst> <sport> <dport>`: 203.0.113.195 — это источник (клиент), а 198.51.100.7 — назначение, на которое подключился proxy.
Heads-up Заголовок PROXY не подписан. Backend должен принимать его только с известных IP доверенных proxy; напрямую доступному backend всё ещё можно скормить поддельную строку.
Этот обработчик кооперируется с connection draining. Что делает srv.Shutdown и какой один продакшен-пробел тут важно отслеживать?
Heads-up Shutdown работает аккуратно: перестаёт принимать и ждёт in-flight запросов до таймаута. Принудительное закрытие — это то, чего вы ИЗБЕГАЕТЕ, вызывая его.
Heads-up Важны оба. Если приложение выйдет раньше, чем LB перестанет маршрутизировать, клиенты получат reset; если drain timeout у LB короче этого context, LB всё равно принудительно закроет соединение в середине запроса. Их нужно согласовать.
Heads-up Shutdown уважает context: на 25 с он возвращается, а оставшиеся соединения принудительно закрываются. Без таймаута зависший поток подвесил бы деплой бесконечно.
Итог
Проблемы LB живут в конфигах и обработчиках. Default в nginx — round-robin, поэтому load-aware политика вроде least_conn (или P2C в upstream) — это исправление для слепого к нагрузке пула. Power-of-two-choices — это две случайные выборки плюс одно сравнение: O(1) и рассинхронизирует стадо, а не просто более дешёвый least-connections. Преамбула PROXY protocol восстанавливает IP клиента для любого протокола транспортного уровня, но ей нужно доверять только с известных IP proxy. А graceful shutdown (закрыть слушатель, дослужить in-flight, таймаут) работает лишь когда окно draining приложения и drain timeout у LB рассчитаны вместе на самое долгоживущее соединение.