Ribbon、LoadBalancer、Nginx 负载均衡策略与实战
负载均衡是微服务架构中的关键组件,通过将请求分发到多个服务实例,实现以下目标:
服务端负载均衡:在服务器端部署负载均衡器(如 Nginx、HAProxy),客户端请求先到达负载均衡器,再由其转发到后端服务。
客户端负载均衡:客户端从注册中心获取服务列表,自己选择一个实例发起请求(如 Ribbon、LoadBalancer)。
1. 轮询(Round Robin)
按顺序依次分配请求到每个实例
2. 随机(Random)
随机选择一个实例处理请求
3. 加权轮询(Weighted Round Robin)
根据实例权重分配请求,性能好的实例权重高
4. 最少连接(Least Connections)
选择当前连接数最少的实例
5. 一致性哈希(Consistent Hash)
根据请求参数哈希,相同参数总是路由到同一实例
6. IP哈希(IP Hash)
根据客户端IP哈希,同一IP总是访问同一实例
Spring Cloud LoadBalancer 是 Spring Cloud 官方推荐的客户端负载均衡器,替代了 Netflix Ribbon。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
// 配置 RestTemplate
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public User getUserById(Long userId) {
// 使用服务名称代替具体地址
String url = "http://user-service/api/users/" + userId;
return restTemplate.getForObject(url, User.class);
}
public List<Product> getProducts() {
String url = "http://product-service/api/products";
return restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<Product>>() {}
).getBody();
}
}
@Configuration
public class LoadBalancerConfig {
// 配置随机策略
@Bean
public ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
return new RandomLoadBalancer(
loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),
name
);
}
}
// 为特定服务指定配置
@LoadBalancerClient(name = "user-service", configuration = LoadBalancerConfig.class)
public class ServiceConfig {
}
public class WeightedLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
private final String serviceId;
public WeightedLoadBalancer(
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
String serviceId) {
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
this.serviceId = serviceId;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
return supplier.get(request).next()
.map(serviceInstances -> processInstanceResponse(serviceInstances));
}
private Response<ServiceInstance> processInstanceResponse(
List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
// 根据权重选择实例
int totalWeight = 0;
for (ServiceInstance instance : instances) {
Map<String, String> metadata = instance.getMetadata();
int weight = Integer.parseInt(metadata.getOrDefault("weight", "1"));
totalWeight += weight;
}
int randomWeight = ThreadLocalRandom.current().nextInt(totalWeight);
int currentWeight = 0;
for (ServiceInstance instance : instances) {
Map<String, String> metadata = instance.getMetadata();
int weight = Integer.parseInt(metadata.getOrDefault("weight", "1"));
currentWeight += weight;
if (randomWeight < currentWeight) {
return new DefaultResponse(instance);
}
}
return new DefaultResponse(instances.get(0));
}
}
虽然 Ribbon 已进入维护模式,但仍被广泛使用。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
# application.yml 配置
user-service:
ribbon:
# 负载均衡策略
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
# 连接超时时间
ConnectTimeout: 3000
# 读取超时时间
ReadTimeout: 3000
# 最大重试次数
MaxAutoRetries: 1
# 切换实例的最大重试次数
MaxAutoRetriesNextServer: 1
# 是否所有操作都重试
OkToRetryOnAllOperations: false
// 1. 轮询策略(默认)
com.netflix.loadbalancer.RoundRobinRule
// 2. 随机策略
com.netflix.loadbalancer.RandomRule
// 3. 重试策略
com.netflix.loadbalancer.RetryRule
// 4. 最低并发策略
com.netflix.loadbalancer.BestAvailableRule
// 5. 可用性过滤策略
com.netflix.loadbalancer.AvailabilityFilteringRule
// 6. 响应时间加权策略
com.netflix.loadbalancer.WeightedResponseTimeRule
// 7. 区域感知策略
com.netflix.loadbalancer.ZoneAvoidanceRule
Nginx 是高性能的 HTTP 和反向代理服务器,常用于服务端负载均衡。
upstream backend_servers {
# 轮询策略(默认)
server 192.168.1.101:8080;
server 192.168.1.102:8080;
server 192.168.1.103:8080;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# 1. 加权轮询
upstream backend_servers {
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080 weight=2;
server 192.168.1.103:8080 weight=1;
}
# 2. IP哈希
upstream backend_servers {
ip_hash;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
# 3. 最少连接
upstream backend_servers {
least_conn;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
# 4. 一致性哈希
upstream backend_servers {
hash $request_uri consistent;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
# 5. 健康检查
upstream backend_servers {
server 192.168.1.101:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 backup; # 备用服务器
}
upstream backend_servers {
# 长连接配置
keepalive 32;
keepalive_timeout 60s;
server 192.168.1.101:8080 weight=5 max_fails=2 fail_timeout=10s;
server 192.168.1.102:8080 weight=3 max_fails=2 fail_timeout=10s;
server 192.168.1.103:8080 backup;
}
server {
listen 80;
location /api/ {
proxy_pass http://backend_servers;
# 超时配置
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;
# 缓冲配置
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
# 长连接
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
OpenFeign 是声明式的 HTTP 客户端,内置负载均衡功能。
// pom.xml 依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
// 启动类
@SpringBootApplication
@EnableFeignClients
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Feign 客户端接口
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/api/users/{id}")
User getUserById(@PathVariable("id") Long id);
@PostMapping("/api/users")
User createUser(@RequestBody User user);
@GetMapping("/api/users")
List<User> getAllUsers();
}
Q: 客户端负载均衡和服务端负载均衡如何选择?
A: 客户端负载均衡性能更好,无单点故障,适合微服务内部调用。服务端负载均衡统一管理,适合对外暴露的 API 网关场景。
Q: 负载均衡如何处理会话保持?
A: 使用 IP 哈希或一致性哈希策略,或者将会话信息存储到 Redis 等共享存储中,实现无状态服务。
负载均衡是微服务高可用的关键技术。通过本课学习,你应该掌握: