Client-side load balancing pattern
Client-side Load Balancing pattern
Client-side load balancing là một mô hình phân phối tải trong đó chính client (ứng dụng gọi API) chịu trách nhiệm chọn server backend để gửi yêu cầu, thay vì dựa vào một Load Balancer trung gian như trong mô hình server-side load balancing.
Cơ chế hoạt động
Client lấy danh sách server khả dụng:
- Danh sách này có thể đến từ một Service Discovery (ví dụ: Eureka, Consul, Zookeeper) hoặc từ một tập cấu hình tĩnh.
Client chọn server để gửi yêu cầu:
Có thể sử dụng các thuật toán như:
Round-robin (luân phiên từng server)
Random (chọn ngẫu nhiên)
Least Connections (chọn server có ít kết nối nhất)
Weighted Load Balancing (chọn server dựa trên trọng số)
Gửi request trực tiếp đến server backend:
- Client kết nối trực tiếp mà không qua một Load Balancer trung gian.
Cập nhật danh sách server (tùy theo cơ chế discovery)
- Client có thể thường xuyên cập nhật danh sách server để tránh gửi request đến các server đã chết.
Ưu điểm của Client-side Load Balancing
Giảm độ trễ: Không cần qua một Load Balancer trung gian, giảm thời gian request.
Tăng khả năng mở rộng: Vì không có Load Balancer trung gian, hệ thống có thể mở rộng dễ dàng hơn.
Linh hoạt: Có thể chọn thuật toán load balancing phù hợp với từng ứng dụng.
Khả năng chịu lỗi tốt hơn: Client có thể loại bỏ server bị lỗi mà không cần phụ thuộc vào Load Balancer.
Nhược điểm của Client-side Load Balancing
Client phức tạp hơn: Client phải tích hợp logic load balancing và service discovery.
Khó kiểm soát: Vì mỗi client tự chọn server, việc giám sát và điều phối có thể khó khăn hơn.
Cần cơ chế cập nhật danh sách server: Nếu danh sách không được cập nhật thường xuyên, client có thể gửi request đến server đã chết.
Các thư viện hỗ trợ Client-side Load Balancing
Spring Cloud LoadBalancer (thay thế Ribbon trong Spring Cloud)
gRPC Load Balancing
Finagle (Twitter)
Hystrix (Netflix) kết hợp với Ribbon
Cách triển khai
Client-side Load Balancing trong Spring Boot thường được thực hiện bằng cách sử dụng Spring Cloud LoadBalancer hoặc Netflix Ribbon (deprecated). Đây là cách để phân phối các yêu cầu giữa các instance của một service một cách cân bằng từ phía client.
Spring Cloud LoadBalancer
hiện là lựa chọn chính thức và được khuyến khích sử dụng trong các dự án Spring Boot cho client-side load balancing
bằng cách sử dụng RestTemplate
hoặc WebClient
với annotation @LoadBalanced
1. Sử dụng Spring Cloud LoadBalancer
Spring Cloud LoadBalancer thay thế Ribbon và giúp tự động cân bằng tải giữa nhiều instance của một service.
1.1. Thêm dependency vào pom.xml
(Spring Boot 2.4+ không cần Ribbon)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
1.2. Định nghĩa RestTemplate
hoặc WebClient
có LoadBalancer
Dùng RestTemplate
(Cách cũ)
@Configuration
public class LoadBalancerConfig {
@Bean
@LoadBalanced // Bật LoadBalancer cho RestTemplate
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Khi gọi API, chỉ cần dùng tên service thay vì địa chỉ cố định:
String response = restTemplate.getForObject("http://user-service/api/users", String.class);
Dùng WebClient
(Khuyến nghị)
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced // Bật LoadBalancer cho WebClient
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
Khi gọi API với WebClient
:
String response = webClientBuilder.build()
.get()
.uri("http://user-service/api/users")
.retrieve()
.bodyToMono(String.class)
.block();
2. Sử dụng Spring Cloud LoadBalancer với Eureka Server (Cân bằng tải giữa nhiều instance)
2.1. Thêm các dependency sau vào tệp pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2.2. Cấu hình Eureka Client
Cấu hình application.yml
để đăng ký với Eureka
spring:
application:
name: user-service
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
preferIpAddress: true
Thêm annotation @EnableEurekaClient
vào lớp main của ứng dụng:
@SpringBootApplication
@EnableEurekaClient
public class LoadBalancerClientApplication {
public static void main(String[] args) {
SpringApplication.run(LoadBalancerClientApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2.3. Gọi các dịch vụ khác
Sử dụng RestTemplate
để gọi các dịch vụ đã đăng ký với Eureka:
@RestController
@RequestMapping("/api")
public class LoadBalancerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/service")
public String callService() {
String url = "http://SERVICE-NAME/endpoint";
return restTemplate.getForObject(url, String.class);
}
}
Nếu có nhiều instance của user-service
, Spring Cloud LoadBalancer sẽ tự động phân phối request giữa các instance.
3. Dùng Kubernetes Load Balancer (Nếu triển khai trên Kubernetes)
Nếu triển khai trên Kubernetes, có thể dùng Kubernetes Service LoadBalancer mà không cần Eureka.
Cấu hình gọi API dùng service-name
trong Kubernetes
String response = restTemplate.getForObject("http://user-service.default.svc.cluster.local/api/users", String.class);
user-service.default.svc.cluster.local
là DNS của service trong Kubernetes.
4. Tuỳ chỉnh Load Balancer (Chọn chiến lược cân bằng tải)
Mặc định, Spring Cloud LoadBalancer sử dụng chiến lược Round-Robin. Có thể tuỳ chỉnh bằng cách:
Cấu hình Round-Robin hoặc Random
Thêm Bean để thay đổi chiến lược:
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,
LoadBalancerClientFactory factory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(factory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
}
Một số chiến lược khác:
Round-Robin (Mặc định)
Random (Ngẫu nhiên)
Weighted (Trọng số)