spring-cloud-starter-loadbalancer 是 Spring Cloud 中的一个组件,它提供了客户端负载均衡的功能。在 Spring Cloud 的早期版本中,Netflix Ribbon 被广泛用作客户端负载均衡器,但随着时间推移和 Netflix Ribbon 进入维护模式,Spring Cloud 社区开始转向更灵活、更易于维护的替代方案。
spring-cloud-starter-loadbalancer 是基于 Spring 5 的 WebClient 构建的,并使用了 Reactor(Spring 5 的反应式编程模型的核心库)来实现异步非阻塞的负载均衡请求。它与 Spring Cloud 的服务发现和配置结合得非常好,可以很容易地与 Eureka、Consul、Nacos 等服务发现组件一起使用。
当将 spring-cloud-starter-loadbalancer 添加到Spring Boot 应用程序中时,可以使用 WebClient.Builder 的 loadBalancer 方法来创建一个具有负载均衡功能的 WebClient 实例。这个 WebClient 实例会自动从服务发现中获取服务实例列表,并使用内置的负载均衡算法(如轮询、随机等)来选择一个服务实例来发送请求。
例如,如果正在使用 Eureka 作为服务发现,并且想要发送一个 GET 请求到名为 “my-service” 的服务,可以这样做:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
@Service
public class MyServiceClient {
@Autowired
private WebClient.Builder webClientBuilder;
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
public String getSomethingFromMyService() {
// 注意这里我们直接使用了 "my-service" 作为 URI,而不是具体的服务实例地址
return webClientBuilder.build()
.get()
.uri("http://my-service/some-endpoint")
.retrieve()
.bodyToMono(String.class)
.block(); // 注意:block() 方法会阻塞当前线程,通常只在非反应式上下文中使用
}
}
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
注意事项
- 阻塞调用:虽然 WebClient 是反应式的,但在某些情况下(如与同步代码交互时),可能需要使用 block() 方法来阻塞当前线程并等待响应。但应尽量避免在反应式上下文中使用 block()。
- 配置:负载均衡器的行为可以通过配置进行定制,包括选择负载均衡算法、设置超时时间等。
- 服务发现:确保应用程序已经正确配置了服务发现组件(如 Eureka、Consul 等),以便 spring-cloud-starter-loadbalancer 能够获取服务实例列表。
- 版本兼容性:注意 spring-cloud-starter-loadbalancer 与其他 Spring Cloud 组件的版本兼容性,确保它们能够协同工作。
通过实现自定义的 ReactorLoadBalancer 来定义自己的负载均衡算法。
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.ReactiveLoadBalancer;
import org.springframework.cloud.client.loadbalancer.ReactiveLoadBalancerFactory;
import org.springframework.cloud.client.ServiceInstanceChooser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import java.util.List;
@Configuration
public class CustomLoadBalancerConfig {
@Bean
public ReactiveLoadBalancer<ServiceInstance> customLoadBalancer(
ReactiveLoadBalancerFactory<ServiceInstance> factory,
ObjectProvider<List<ServiceInstance>> serviceInstances) {
return new ReactiveLoadBalancer<ServiceInstance>() {
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
// 这里是自定义的负载均衡算法实现
// 例如,我们可以简单地返回服务实例列表中的第一个实例
return Mono.justOrEmpty(serviceInstances.getIfAvailable())
.flatMapMany(List::stream)
.firstElement() // 或者可以实现自己的选择逻辑
.map(Response::just);
}
// 其他必要的方法(如 recordStats, filter, etc.)可以根据需要进行实现
};
}
// 如果想要为特定的服务配置自定义的负载均衡器,
// 可以通过 ServiceId 来区分并返回不同的 ReactiveLoadBalancer 实例
// 例如,public ReactiveLoadBalancer customLoadBalancerForServiceX(...) {...}
}
spring-cloud-starter-loadbalancer 提供了多种负载均衡算法,包括轮询、随机和自定义策略等。这些算法可以根据实际需求进行选择和配置,以满足不同的负载均衡需求。同时,与 Nacos 服务发现组件的集成还提供了权重负载均衡器的功能,进一步增加了负载均衡的灵活性和可定制性。开发者可以根据自己的业务场景和需求选择适合的负载均衡算法,并对其进行适当的配置和优化,以实现更高效、更可靠的微服务调用。
从 Spring Cloud Greenwich 版本开始,Spring Cloud 引入了对 Project Reactor 的支持,并将负载均衡器从传统的阻塞式(基于 Ribbon)转变为反应式(基于 spring-cloud-starter-loadbalancer)。
反应式编程是一种异步、非阻塞的编程范式,它使用数据流(streams)和变化传播(propagation of change)来处理数据。在反应式编程中,数据不是通过传统的调用和返回机制来传递的,而是通过异步数据流在组件之间传递。
在 spring-cloud-starter-loadbalancer 中,反应式编程主要体现在以下几个方面:
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@RestController
public class MyController {
private final LoadBalancerClient loadBalancerClient;
public MyController(LoadBalancerClient loadBalancerClient) {
this.loadBalancerClient = loadBalancerClient;
}
@GetMapping("/call-service")
public Mono<String> callService() {
// 获取服务实例
ServiceInstance serviceInstance = loadBalancerClient.choose("my-service").block();
// 使用 WebClient 发起反应式请求
WebClient webClient = WebClient.builder()
.baseUrl(serviceInstance.getUri().toString())
.build();
return webClient.get()
.uri("/some-endpoint")
.retrieve()
.bodyToMono(String.class);
}
}
注意:上面的示例中使用了 block() 方法来同步获取服务实例,这在实际应用中可能不是最佳实践。通常,应该在整个调用链中保持反应式编程的异步特性。但是,为了简化示例,这里使用了 block() 方法。在实际应用中,应该将服务实例的获取和请求的发起都转换为反应式操作。
Spring Cloud 应用程序中,spring-cloud-starter-loadbalancer 通常与 spring-cloud-starter-openfeign 或其他 HTTP 客户端(如 WebClient)一起使用,以支持对服务发现的客户端进行负载均衡的调用。
当使用 OpenFeign 声明式 HTTP 客户端时,spring-cloud-starter-loadbalancer 会自动集成以提供负载均衡功能。只需在 pom.xml 或 build.gradle 文件中包含相应的依赖,并在 Feign 客户端接口上使用 @FeignClient 注解指定服务名。
Maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
dependencies>
Feign 客户端
@FeignClient(name = "my-service")
public interface MyServiceClient {
// 定义 HTTP 方法
@GetMapping("/some-endpoint")
Mono<String> getSomething();
}
使用 WebClient 作为 HTTP 客户端,可以通过 spring-cloud-starter-loadbalancer 来实现服务间的负载均衡调用。需要创建一个 WebClient.Builder bean,并使用 LoadBalancerExchangeFilterFunction 来自动处理服务发现和负载均衡。
配置 WebClient Bean
@Bean
public WebClient.Builder webClientBuilder(LoadBalancerClient loadBalancerClient) {
return WebClient.builder()
.baseUrl("lb://my-service") // 使用 'lb://' 前缀启用负载均衡
.filter(new LoadBalancerExchangeFilterFunction(loadBalancerClient));
}
使用 WebClient 发起请求
@Autowired
private WebClient.Builder webClientBuilder;
public Mono<String> callService() {
WebClient webClient = webClientBuilder.build();
return webClient.get()
.uri("/some-endpoint")
.retrieve()
.bodyToMono(String.class);
}
注意事项
- 确保 Spring Cloud 版本支持 spring-cloud-starter-loadbalancer。
- 从使用 Ribbon 迁移到 spring-cloud-starter-loadbalancer,请注意两者之间的配置差异和 API 更改。
- 在使用 WebClient 时,确保使用了正确的 URL 前缀(lb://)来启用负载均衡。
- 在自定义负载均衡器时,确保实现是线程安全的,并且能够处理并发请求。