java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringCloud负载均衡spring-cloud-starter-loadbalancer

SpringCloud负载均衡spring-cloud-starter-loadbalancer解读

作者:王小工

这篇文章主要介绍了SpringCloud负载均衡spring-cloud-starter-loadbalancer使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

简述

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() 方法会阻塞当前线程,通常只在非反应式上下文中使用  
    }  
}

主要特点

使用

  1. 添加依赖:在 Maven 或 Gradle 项目中添加 spring-cloud-starter-loadbalancer 依赖。
		<!-- SpringCloud Loadbalancer -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>
  1. 配置 WebClient:使用 @LoadBalanced 注解来标记一个 WebClient.Builder Bean,以便将其配置为支持负载均衡。
  2. 发送请求:通过 WebClient 发送请求时,使用服务名称(而不是具体的服务实例地址)作为 URI 的主机部分。
  3. 自定义负载均衡算法:如果需要,可以自定义负载均衡算法,并通过配置或编程方式将其应用到 WebClient 上。

注意事项:

负载均衡算法

1. 轮询负载均衡策略(Round Robin)

2. 随机负载均衡策略(Random)

3. 自定义负载均衡策略

4. Nacos 权重负载均衡器

5. 自定义算法

通过实现自定义的 ReactorLoadBalancer 来定义自己的负载均衡算法。

  1. 定义自定义的负载均衡器:需要实现 ReactorLoadBalancer 接口或扩展现有的实现(如 RoundRobinLoadBalancer)。
  2. 实现 choose 方法:这是负载均衡算法的核心,它接收一个请求(通常是一个 Request 对象)和一个服务实例列表(ServiceInstanceListSupplier),并返回一个 Mono,表示选定的服务实例。
  3. 配置自定义的负载均衡器:需要将自定义的负载均衡器配置为 Spring Cloud 的默认负载均衡器。这通常是通过注册一个 ReactorLoadBalancer 的 Bean 来完成的。

示例:

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<ServiceInstance> 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 中,反应式编程主要体现在以下几个方面:

  1. 非阻塞调用:与传统的基于 Ribbon 的阻塞式负载均衡器不同,spring-cloud-starter-loadbalancer 使用反应式编程模型来执行非阻塞的负载均衡请求。这意味着它不会阻塞线程等待响应,而是异步地处理请求和响应。
  2. 响应式类型:负载均衡器的 API 使用了反应式类型,如 Mono 和 Flux,它们是 Project Reactor 提供的反应式类型。Mono 用于表示 0 或 1 个元素的异步序列,而 Flux 用于表示 0 到 N 个元素的异步序列。
  3. 背压(Backpressure):反应式编程支持背压机制,即消费者可以控制生产者生成数据的速度。这在处理大量并发请求时非常有用,可以避免因生产者过快生成数据而导致消费者处理不过来。
  4. 错误处理:反应式编程提供了丰富的错误处理机制,如 onErrorResume、retry 等操作符,可以在发生错误时优雅地处理异常情况。
  5. 组合和转换:Mono 和 Flux 提供了丰富的操作符,用于组合和转换异步数据流。这使得可以灵活地处理负载均衡请求和响应,满足各种复杂的业务需求。

简单的示例:

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() 方法。在实际应用中,应该将服务实例的获取和请求的发起都转换为反应式操作。

与 OpenFeign 集成

Spring Cloud 应用程序中,spring-cloud-starter-loadbalancer 通常与 spring-cloud-starter-openfeign 或其他 HTTP 客户端(如 WebClient)一起使用,以支持对服务发现的客户端进行负载均衡的调用。

当使用 OpenFeign 声明式 HTTP 客户端时,spring-cloud-starter-loadbalancer 会自动集成以提供负载均衡功能。只需在 pom.xml 或 build.gradle 文件中包含相应的依赖,并在 Feign 客户端接口上使用 @FeignClient 注解指定服务名。

<dependencies>  
   <!-- ... 其他依赖 ... -->  
   <dependency>  
       <groupId>org.springframework.cloud</groupId>  
       <artifactId>spring-cloud-starter-openfeign</artifactId>  
   </dependency>  
   <dependency>  
       <groupId>org.springframework.cloud</groupId>  
       <artifactId>spring-cloud-starter-loadbalancer</artifactId>  
   </dependency>  
   <!-- ... 其他依赖 ... -->  
</dependencies>
@FeignClient(name = "my-service")  
public interface MyServiceClient {  
   // 定义 HTTP 方法  
   @GetMapping("/some-endpoint")  
   Mono<String> getSomething();  
}

WebClient 集成

使用 WebClient 作为 HTTP 客户端,可以通过 spring-cloud-starter-loadbalancer 来实现服务间的负载均衡调用。需要创建一个 WebClient.Builder bean,并使用 LoadBalancerExchangeFilterFunction 来自动处理服务发现和负载均衡。

@Bean  
public WebClient.Builder webClientBuilder(LoadBalancerClient loadBalancerClient) {  
    return WebClient.builder()  
            .baseUrl("lb://my-service") // 使用 'lb://' 前缀启用负载均衡  
            .filter(new LoadBalancerExchangeFilterFunction(loadBalancerClient));  
}
@Autowired  
private WebClient.Builder webClientBuilder;  
  
public Mono<String> callService() {  
    WebClient webClient = webClientBuilder.build();  
    return webClient.get()  
            .uri("/some-endpoint")  
            .retrieve()  
            .bodyToMono(String.class);  
}

注意事项:

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文