Какие знаешь стратегии Load Balancing?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Стратегии Load Balancing
Load Balancing (балансировка нагрузки) - критический компонент при масштабировании приложений. Распределяет входящие запросы между несколькими серверами.
1. Round Robin (Циклическое распределение)
Самый простой способ - запросы распределяются по кругу:
Запрос 1 -> Сервер 1
Запрос 2 -> Сервер 2
Запрос 3 -> Сервер 3
Запрос 4 -> Сервер 1
Запрос 5 -> Сервер 2
...
Реализация в Spring Cloud:
@Configuration
public class LoadBalancerConfig {
@Bean
public RestTemplate restTemplate(
@LoadBalanced RestTemplateBuilder builder) {
return builder.build(); // Использует Round Robin
}
}
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
public User getUser(String id) {
// Автоматически распределяется между инстансами USER-SERVICE
return restTemplate.getForObject(
"http://USER-SERVICE/users/" + id,
User.class
);
}
}
Характеристики:
- Плюсы: простая, справедливая
- Минусы: не учитывает нагрузку на серверы
- Когда использовать: все серверы одинаковые
2. Weighted Round Robin (Взвешенное распределение)
Некоторым серверам даётся больший вес:
Сервер 1 (вес 3) -> получает 3 запроса из 6
Сервер 2 (вес 2) -> получает 2 запроса из 6
Сервер 3 (вес 1) -> получает 1 запрос из 6
Конфигурация в Nginx:
upstream backend {
server server1.example.com weight=3;
server server2.example.com weight=2;
server server3.example.com weight=1;
}
server {
location / {
proxy_pass http://backend;
}
}
Когда использовать: разные серверы имеют разную мощность
3. Least Connections (Наименьше соединений)
Запрос идёт к серверу с минимальным количеством активных соединений:
Сервер 1: 5 соединений
Сервер 2: 2 соединения <- Новый запрос
Сервер 3: 8 соединений
Реализация (Nginx):
upstream backend {
least_conn;
server server1.example.com;
server server2.example.com;
server server3.example.com;
}
Когда использовать: долгоживущие соединения (WebSocket)
4. IP Hash (Привязка по IP)
Запросы от одного IP всегда идут на один сервер:
IP 192.168.1.1 -> hash() -> Сервер 1 (всегда)
IP 192.168.1.2 -> hash() -> Сервер 2 (всегда)
IP 192.168.1.1 -> hash() -> Сервер 1 (одинаков)
Nginx конфигурация:
upstream backend {
ip_hash;
server server1.example.com;
server server2.example.com;
server server3.example.com;
}
Плюсы:
- Каждый клиент попадает на один сервер
- Полезно для сессионных данных
Минусы:
- Неравномерная нагрузка
- При добавлении/удалении сервера - переоценка
Когда использовать: sticky sessions без shared cache
5. Least Response Time (Минимальное время ответа)
Запрос идёт к серверу с наименьшим временем ответа:
Сервер 1: avg response time = 50ms
Сервер 2: avg response time = 100ms <- ЗАНЯТ
Сервер 3: avg response time = 30ms <- Новый запрос
Nginx (требует módulo):
upstream backend {
least_time last_byte;
server server1.example.com;
server server2.example.com;
server server3.example.com;
}
Когда использовать: неоднородные серверы с разными
6. Random (Случайное распределение)
Запрос случайно направляется на один из серверов:
private List<Server> servers = Arrays.asList(
new Server("server1"),
new Server("server2"),
new Server("server3")
);
public Server selectServer() {
Random random = new Random();
return servers.get(random.nextInt(servers.size()));
}
Nginx:
upstream backend {
random;
server server1.example.com;
server server2.example.com;
}
Плюсы: простая, хорошо распределяет при случайных запросах Минусы: не учитывает состояние сервера
7. Consistent Hashing (Согласованное хеширование)
Используется для распределённых систем:
Ключ "user:123" -> hash() -> позиция на кольце
Кольцо (hash space):
Server1 (hash=10)
/ \
Server3 Server2
(hash=80) (hash=40)
"user:123" -> hash=50 -> nearest clockwise = Server2
Плюсы:
- При добавлении сервера - минимально перехешируется
- Идеально для кэширования
Когда использовать: распределённое кеширование (Redis), sharding БД
8. Session Affinity / Sticky Sessions
Сессия привязывается к одному серверу:
Запрос 1 (no cookie) -> Server1 (присваивает cookie с ID)
Запрос 2 (cookie=server1) -> Server1 (заходит на тот же)
Запрос 3 (cookie=server1) -> Server1
Spring Session решение (Shared Session Storage):
@Configuration
public class SessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
// Конфигурация
spring.session.store-type=redis
Nginx Sticky Session:
upstream backend {
server server1.example.com;
server server2.example.com;
sticky cookie srv_id expires=1h domain=.example.com path=/ secure httponly;
}
Минусы: снижает масштабируемость, нужен общий storage
9. Resource Based (На основе ресурсов)
Клиент выбирает сервер на основе доступных ресурсов:
public class ResourceBasedLoadBalancer {
public Server selectServer(List<Server> servers) {
return servers.stream()
.min(Comparator.comparing(server ->
calculateLoad(server.getCpuUsage(),
server.getMemoryUsage(),
server.getDiskSpace())
))
.orElse(servers.get(0));
}
private double calculateLoad(double cpu, double memory, double disk) {
return (cpu * 0.5) + (memory * 0.3) + (disk * 0.2);
}
}
Когда использовать: гетерогенные серверы с разным железом
10. Geographic Load Balancing (Географическое)
Запросы направляются на ближайший сервер (по geography):
Пользователь в Москве -> московский data center
Пользователь в NY -> NY data center
Пользователь в Tokyo -> Tokyo data center
Реализация с DNSmasq:
# GeoDNS маршрутизирует на основе IP geo-локации
В Java через CDN:
// CloudFlare, AWS CloudFront автоматически handles
11. Сравнение стратегий
╔════════════════════════╦═══════════════════╦════════════════╦═══════════════╗
║ Стратегия ║ Сложность ║ Справедливость ║ Лучше всего ║
╠════════════════════════╬═══════════════════╬════════════════╬═══════════════╣
║ Round Robin ║ Низкая ║ Хорошая ║ Стандартная ║
║ Weighted RR ║ Низкая ║ Отличная ║ Разные мощ ║
║ Least Connections ║ Средняя ║ Отличная ║ WebSocket ║
║ IP Hash ║ Низкая ║ Плохая ║ Sticky session║
║ Least Response Time ║ Средняя ║ Отличная ║ Динамическая ║
║ Consistent Hashing ║ Высокая ║ Отличная ║ Шардирование ║
║ Resource Based ║ Высокая ║ Отличная ║ Гетерогенные ║
╚════════════════════════╩═══════════════════╩════════════════╩═══════════════╝
12. Health Check (Проверка здоровья)
Любой балансировщик должен проверять доступность сервера:
@Service
public class HealthCheckService {
@Scheduled(fixedDelay = 5000)
public void checkServerHealth() {
for (Server server : servers) {
try {
ResponseEntity<HealthStatus> response =
restTemplate.getForEntity(
server.getHealthUrl(),
HealthStatus.class
);
server.setHealthy(response.getStatusCode().is2xxSuccessful());
} catch (Exception e) {
server.setHealthy(false);
}
}
}
}
Nginx health check:
upstream backend {
server server1.example.com;
server server2.example.com;
check interval=3000 rise=2 fall=5 timeout=1000 type=http;
check_http_send "GET /health HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx;
}
13. Общие рекомендации
- Простые случаи: Round Robin
- HTTP запросы: Least Connections
- Сессионные данные: Shared Storage + Round Robin (не IP Hash)
- Кэширование: Consistent Hashing
- Микросервисы: Spring Cloud Load Balancer (интеграция с Service Discovery)
- Production: обязательно health checks
- Масштабирование: используй с автоскейлингом
14. Spring Cloud LoadBalancer Example
@Configuration
public class LoadBalancerConfiguration {
@Bean
public LoadBalancerClient loadBalancerClient(
ServiceInstanceListSupplier supplier) {
return new BlockingLoadBalancerClient(
new RoundRobinLoadBalancer(
supplier,
"USER-SERVICE"
)
);
}
}