一、负载均衡的基本概念
1.1 什么是负载均衡
负载均衡是指将流量分发到多个服务器以均衡负载,提升系统的可用性和性能。它可以在多个层级实现:
- 客户端负载均衡:请求由客户端直接分发给不同的服务实例。
- 服务端负载均衡:请求由负载均衡器(如 Nginx)转发到服务实例。
1.2 Spring Cloud 的负载均衡
Spring Cloud 提供了两种负载均衡机制:
- Ribbon:Spring Cloud Netflix 提供的客户端负载均衡器,已在新版 Spring Cloud 中被弃用。
- Spring Cloud LoadBalancer:Spring Cloud 提供的官方负载均衡实现,从 Spring Cloud 2020 开始推荐使用。
二、负载均衡的策略
Spring Cloud 支持多种负载均衡策略,常见的有:
- 轮询(RoundRobin):
- 按照顺序依次将请求分发到各服务实例。
- 随机(Random):
- 随机选择一个服务实例处理请求。
- 权重(Weighted):
- 根据服务实例的权重分发请求。
- 基于响应时间(Best Available):
- 优先将请求分发到响应时间最短的实例。
开发者可以选择默认策略,也可以通过自定义策略满足业务需求。
三、Spring Cloud LoadBalancer 配置与使用
3.1 添加依赖
在 Maven 项目中添加 spring-cloud-starter-loadbalancer
依赖。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
3.2 配置 Eureka 注册中心
application.yml 示例:
spring:
application:
name: client-service
cloud:
loadbalancer:
retry: true # 启用重试机制
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
3.3 配置服务提供者
注册多个实例到 Eureka,模拟负载均衡。假设有两个服务实例:
provider-service1
:运行在http://localhost:8081
provider-service2
:运行在http://localhost:8082
四、Spring Cloud LoadBalancer 的实现
以下示例通过 RestTemplate
和 WebClient
演示负载均衡的使用。
4.1 RestTemplate 方式
创建配置类
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 开启负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
使用 RestTemplate 调用服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class LoadBalancerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/invoke")
public String invokeProvider() {
// 通过服务名调用服务
String url = "http://provider-service/hello";
return restTemplate.getForObject(url, String.class);
}
}
4.2 WebClient 方式
创建配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient.Builder webClientBuilder() {
return WebClient.builder();
}
}
使用 WebClient 调用服务
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
@RestController
public class LoadBalancerController {
@Autowired
private WebClient.Builder webClientBuilder;
@GetMapping("/invokeWebClient")
public String invokeProviderWithWebClient() {
return webClientBuilder.build()
.get()
.uri("http://provider-service/hello")
.retrieve()
.bodyToMono(String.class)
.block();
}
}
五、自定义负载均衡策略
5.1 自定义配置类
通过实现 ReactorServiceInstanceLoadBalancer
接口来自定义负载均衡策略。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
import java.util.List;
import java.util.Random;
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final ServiceInstanceListSupplier serviceInstanceListSupplier;
private final Random random = new Random();
public CustomLoadBalancer(ServiceInstanceListSupplier serviceInstanceListSupplier) {
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return serviceInstanceListSupplier.get()
.next()
.map(this::getRandomInstance);
}
private Response<ServiceInstance> getRandomInstance(List<ServiceInstance> instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
ServiceInstance instance = instances.get(random.nextInt(instances.size()));
return new DefaultResponse(instance);
}
}
5.2 注册自定义策略
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@LoadBalancerClient(name = "provider-service", configuration = CustomLoadBalancerConfig.class)
public class CustomLoadBalancerConfig {
@Bean
public ReactorServiceInstanceLoadBalancer customLoadBalancer(ServiceInstanceListSupplier supplier) {
return new CustomLoadBalancer(supplier);
}
}
六、负载均衡的测试
- 启动服务提供者实例:
- 分别运行两个服务实例,端口为
8081
和8082
。
- 分别运行两个服务实例,端口为
- 启动客户端服务:
- 启动带负载均衡配置的客户端。
- 测试负载均衡:
- 访问
http://localhost:8080/invoke
或http://localhost:8080/invokeWebClient
,观察服务响应是否轮流来自8081
和8082
。
- 访问
七、负载均衡的优缺点
优点
- 性能提升:通过分发流量减轻单点负载压力。
- 容错性增强:服务实例宕机时,流量可分发至其他可用实例。
- 扩展性高:可动态添加或删除实例,支持水平扩展。
缺点
- 复杂性增加:需要额外的服务注册和发现机制。
- 一致性问题:客户端负载均衡可能导致分布式数据的一致性问题。
- 延迟:请求需要经过多次解析和路由,可能增加延迟。
八、总结
Spring Cloud 的负载均衡功能为微服务架构提供了高效的流量分发能力。无论是通过 RestTemplate 还是 WebClient,开发者都可以方便地实现客户端负载均衡。同时,Spring Cloud LoadBalancer 支持灵活的负载均衡策略,适应各种业务需求。