<返回目录     Powered by claud/xia兄

第4课: 负载均衡

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 LoadBalancer 是 Spring Cloud 官方推荐的客户端负载均衡器,替代了 Netflix Ribbon。

1. 基础配置

// 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();
    }
}

2. 使用 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();
    }
}

3. 自定义负载均衡策略

@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 {
}

4. 自定义负载均衡算法

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 负载均衡(经典方案)

虽然 Ribbon 已进入维护模式,但仍被广泛使用。

5. 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

6. Ribbon 负载均衡策略

// 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 服务端负载均衡

Nginx 是高性能的 HTTP 和反向代理服务器,常用于服务端负载均衡。

7. Nginx 基础配置

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;
    }
}

8. Nginx 负载均衡策略

# 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;  # 备用服务器
}

9. Nginx 高级配置

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 声明式服务调用

OpenFeign 是声明式的 HTTP 客户端,内置负载均衡功能。

10. OpenFeign 配置

// 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();
}
最佳实践:

实践练习

练习任务:
  1. 实现轮询负载均衡:使用 LoadBalancer 实现基础轮询策略
  2. 自定义加权策略:根据服务实例的元数据权重分配请求
  3. 配置 Nginx 负载均衡:搭建 Nginx 反向代理,实现服务端负载均衡
  4. 测试故障转移:停止部分实例,观察负载均衡的自动切换
  5. 性能对比:对比客户端负载均衡和服务端负载均衡的性能差异
  6. 实现一致性哈希:确保相同用户的请求总是路由到同一实例
  7. 集成 OpenFeign:使用声明式客户端简化服务调用
  8. 压力测试:使用 JMeter 测试负载均衡在高并发下的表现

常见问题

Q: 客户端负载均衡和服务端负载均衡如何选择?

A: 客户端负载均衡性能更好,无单点故障,适合微服务内部调用。服务端负载均衡统一管理,适合对外暴露的 API 网关场景。

Q: 负载均衡如何处理会话保持?

A: 使用 IP 哈希或一致性哈希策略,或者将会话信息存储到 Redis 等共享存储中,实现无状态服务。

总结

负载均衡是微服务高可用的关键技术。通过本课学习,你应该掌握: