Как выбирать инстанс для обработки запроса при нескольких backend-сервисах?
Что проверяют
Понимаете ли вы, где принимается решение (на балансировщике, на клиенте или в сайдкаре) и по какому алгоритму. Следом обычно спрашивают, что делать с упавшим инстансом, как быть с сессиями и почему gRPC через обычный Kubernetes Service грузит один под.
Как отвечать
Сначала место решения:
- серверная балансировка: Nginx, HAProxy, облачный балансировщик или Kubernetes Service. Клиент знает один адрес, остальное от него скрыто;
- клиентская: сервис сам берёт список инстансов из реестра (Eureka, Consul, DNS в Kubernetes) и выбирает. В Spring это Spring Cloud LoadBalancer, по умолчанию
RoundRobinLoadBalancer, а Ribbon из Spring Cloud удалён; - service mesh: Envoy в Istio выбирает инстанс за приложение и заодно даёт ретраи и mTLS.
Потом алгоритм:
- round robin и weighted: просто и ровно, пока запросы и машины одинаковые;
- least connections и least requests: лучше, когда запросы разной тяжести;
- power of two choices: берут два случайных инстанса и отправляют на менее загруженный. Почти как least requests, но без глобального состояния, на этом построен
LEAST_REQUESTв Envoy; - consistent hashing: один ключ (пользователь, тенант) всегда попадает на один инстанс, это полезно для локального кеша, а при добавлении узла переезжает малая часть ключей.
И обязательная обвязка: health checks и outlier detection, чтобы выводить больные инстансы из ротации, таймауты, ретраи только для идемпотентных запросов и с ограниченным бюджетом, circuit breaker в Resilience4j.
Где спотыкаются
Отвечают «поставим балансировщик» без алгоритма и без обработки отказов. Инстанс, который за миллисекунду отвечает ошибкой, в least requests выглядит самым свободным и собирает на себя больше запросов, чем здоровые.
Не знают, что Kubernetes Service балансирует соединения, а не запросы. HTTP/2 и gRPC держат одно долгое соединение, и вся нагрузка клиента уходит на один под.
Хранят сессию в памяти инстанса и спасаются sticky sessions. Инстанс падает, пользователи теряют состояние, поэтому его выносят в Redis или базу.