前言
在当今互联网软件开发的大环境下,微服务架构已然成为主流。而在微服务架构中,确保系统的高可用性、高性能以及良好的扩展性是至关重要的。其中,负载均衡技术作为实现这些目标的关键手段之一,发挥着不可或缺的作用。今天,我们就来深入探讨一下 Spring Cloud 中客户端负载均衡的相关知识,看看如何实现客户端负载均衡,让你的微服务架构更加稳固和高效。
负载均衡的重要性及类型
在系统架构里,负载均衡是极为关键且必须实施的环节。它能够有效缓解网络压力,提升系统的处理能力,实现系统的扩容,是保障系统高可用的重要手段。一般而言,我们所说的负载均衡主要指服务端负载均衡,它又可细分为硬件负载均衡和软件负载均衡。硬件负载均衡通过在服务器节点间安装如 F5 等专门设备来实现;软件负载均衡则借助在服务器上安装具备均衡负载功能的软件模块,像 Nginx 等。无论是硬件还是软件负载均衡,其架构模式通常都类似,负载均衡设备或软件模块会维护一份可用服务端清单,并通过心跳检测剔除故障节点,以保证清单中的服务端均可正常访问。当客户端发送请求时,负载均衡设备依据特定算法(如线性轮询、按权重负载、按流量负载等)从清单中选取一个服务端地址进行转发。
而客户端负载均衡与服务端负载均衡最大的区别就在于服务清单的存储位置。在客户端负载均衡中,所有客户端节点各自维护自身要访问的服务端清单,这些清单来自服务注册中心,例如 Eureka 服务端。同样,客户端负载均衡也需要通过心跳机制来维护服务端清单的健康性,不过此步骤需与服务注册中心协同完成。
Spring Cloud 中的客户端负载均衡 ——Ribbon
Spring Cloud Ribbon 是一款基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现。通过 Spring Cloud 的封装,我们能够轻松地将面向服务的 REST 模板请求自动转换为客户端负载均衡的服务调用。虽然 Ribbon 只是一个工具类框架,无需像服务注册中心、配置中心、API 网关那样独立部署,但在几乎每一个由 Spring Cloud 构建的微服务和基础设施中都能看到它的身影。因为微服务间的调用、API 网关的请求转发等操作,实际上很多都是借助 Ribbon 来完成的,后续我们会介绍的 Feign,同样也是基于 Ribbon 实现的工具。由此可见,深入理解和熟练运用 Spring Cloud Ribbon,对于我们利用 Spring Cloud 构建微服务具有重要意义。
(一)Ribbon 的工作原理
Spring Cloud Ribbon 的底层利用了一个拦截器,用于拦截 RestTemplate 发出的请求,并对地址进行修改。其基本流程如下:
- 拦截 RestTemplate 请求,例如 http://userservice/user/1 。
- RibbonLoadBalancerClient 从请求 url 中提取服务名称,此例中为 user - service。
- DynamicServerListLoadBalancer 依据服务名称 user - service 到 Eureka 服务注册中心拉取服务列表。
- Eureka 返回服务列表,如localhost:8081、localhost:8082 。
- IRule 运用内置的负载均衡规则,从列表中挑选一个服务实例,比如选择了localhost:8081 。
- RibbonLoadBalancerClient 修改请求地址,将 userservice 替换为具体的实例地址localhost:8081,得到http://localhost:8081/user/1 ,进而发起真实请求。
(二)负载均衡策略 IRule
IRule 接口决定了 Ribbon 的负载均衡策略,该接口拥有众多不同的实现类,常见的如下:
- RoundRobinRule:简单轮询服务列表来选择服务器,这是 Ribbon 默认的负载均衡规则。它会按照顺序依次选择服务实例,当到达列表末尾时,重新从第一个实例开始循环选择。例如,假设有服务实例 A、B、C,按照 RoundRobinRule 的策略,请求依次会被分配到 A、B、C、A、B、C…… 这样的顺序。
- AvailabilityFilteringRule:该规则会忽略以下两种服务器:一是在默认情况下,如果一台服务器 3 次连接失败,它将被设置为 “短路” 状态,此状态会持续 30 秒,若再次连接失败,短路持续时间会呈几何级增加;二是并发数过高的服务器。如果一个服务器的并发连接数超出设定上限(可通过客户端的..ActiveConnectionsLimit 属性配置),配置了 AvailabilityFilteringRule 规则的客户端同样会将其忽略。
- WeightedResponseTimeRule:为每个服务器赋予一个权重值。服务器的响应时间越长,其权重越小。该规则会随机选择服务器,但权重值会影响选择结果。例如,有服务器 A、B、C,A 的响应时间短,权重高,B 和 C 响应时间相对较长,权重较低。那么在随机选择时,A 被选中的概率会相对较高。
- ZoneAvoidanceRule:以区域可用的服务器为基础进行服务器选择。它会先使用 Zone 对服务器进行分类(Zone 可理解为一个机房、一个机架等),然后在 Zone 内的多个服务中进行轮询。比如,有两个 Zone,Zone1 中有服务器 A1、A2,Zone2 中有服务器 B1、B2。当请求到来时,它可能先选择 Zone1,然后在 Zone1 中的 A1、A2 间进行轮询选择。
- BestAvailableRule:此规则会忽略那些处于短路状态的服务器,并选择并发数较低的服务器。例如,当有多个服务实例时,它会遍历所有实例,排除掉处于短路状态的,然后从剩余实例中挑选并发数最少的那个。
- RandomRule:随机选择一个可用的服务器。在众多服务实例中,完全随机地挑选一个来处理请求。
- RetryRule:实现了一种重试机制的选择逻辑。在其内部默认使用了 RoundRobinRule 实例。当按照 RoundRobinRule 未选择到可用服务实例时,它会在指定的重试次数内进行重试。
(三)修改负载均衡策略
在实际应用中,我们可以根据业务需求修改负载均衡策略,有以下两种方式:
代码方式:以 order - service 服务为例,在其 OrderApplication 类中定义一个新的 IRule,即可实现全局配置。例如:
public IRule randomRule(){ return new RandomRule(); }这段代码将负载均衡策略修改为了 RandomRule,即随机选择服务实例。
配置文件方式:在 order - service 的 application.yml 文件中添加配置,可针对某个微服务进行配置。比如:
userservice: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule上述配置针对 userservice 服务,将其负载均衡规则修改为了 RandomRule。不过在实际项目中,通常优先使用默认的负载均衡规则,尽量避免不必要的修改,以免引入潜在问题。
(四)懒加载和饥饿加载
Ribbon 默认采用懒加载模式,即首次访问时才会创建 LoadBalanceClient,这会导致首次请求时间较长。而饥饿加载则是在项目启动时就创建 LoadBalanceClient,从而减少首次访问的耗时。通过在配置文件中添加如下配置可开启饥饿加载:
ribbon: eager - load: enabled: true clients: userservice这里不仅开启了饥饿加载,还指定了对 userservice 服务进行饥饿加载。
Spring Cloud LoadBalancer——Ribbon 的替代方案
需要注意的是,Ribbon 目前已进入维护模式,未来的替换方案是 Spring Cloud LoadBalancer。Spring Cloud LoadBalancer 为我们提供了一种更现代化的客户端负载均衡解决方案。
(一)Spring Cloud LoadBalancer 原理
LoadBalancerClient 作为负载均衡客户端,负责执行负载均衡逻辑,从服务列表中挑选一个服务地址进行调用。其内部包含 execute () 方法用于执行请求,reconstructURI () 方法用于重构 URL。
在 Spring Cloud LoadBalancer 中,LoadBalancerClient 的实现类是
BlockingLoadBalancerClient,它实现了 ServiceInstanceChooser 接口中的 choose () 方法。通过 LoadBalancerClientFactory 工厂类获取具体的负载均衡器实例,然后调用 loadBalancer.choose (request) 方法,依据负载均衡算法选择下一个服务器,从而完成负载均衡操作。
在 Spring Cloud LoadBalancer 中,ReactorLoadBalancer 接口继承自 ReactiveLoadBalancer 接口,并且
ReactorServiceInstanceLoadBalancer 继承了 ReactorLoadBalancer 接口,同时实现了 RandomLoadBalancer(随机负载均衡器)和 RoundRobinLoadBalancer(轮询负载均衡器)等方法。
(二)自定义负载均衡器
若要自定义负载均衡器,根据其类图结构,我们只需在自定义配置轮询方法时重定义
ReactorServiceInstanceLoadBalancer 接口即可。例如,若要实现一个随机负载均衡器,可以这样做:
import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.stereotype.Component; import reactor.core.publisher.Mono; import java.util.List; import java.util.Random; @Component public class CustomRandomLoadBalancer implements ReactorServiceInstanceLoadBalancer { private final ServiceInstanceListSupplier serviceInstanceListSupplier; private final Random random = new Random(); public CustomRandomLoadBalancer(ServiceInstanceListSupplier serviceInstanceListSupplier) { this.serviceInstanceListSupplier = serviceInstanceListSupplier; } @Override public Mono<ServiceInstance> choose(Request request) { return Mono.fromSupplier(() -> { List<ServiceInstance> serviceInstances = serviceInstanceListSupplier.get().get(); if (serviceInstances.isEmpty()) { return null; } int index = random.nextInt(serviceInstances.size()); return serviceInstances.get(index); }); } }在项目启动类上添加 @LoadBalancerClient 注解,指定服务名和自定义配置类:
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.client.loadbalancer.LoadBalancerClients; @SpringBootApplication @LoadBalancerClients(name = "your - service - name", configuration = CustomLoadBalancerConfig.class) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }其中,CustomLoadBalancerConfig 类用于配置自定义的负载均衡器:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; @Configuration public class CustomLoadBalancerConfig { @Bean public ReactorServiceInstanceLoadBalancer customLoadBalancer() { return new CustomRandomLoadBalancer(); } }通过以上步骤,我们就实现了一个自定义的随机负载均衡器。
总结
在 Spring Cloud 的微服务架构中,客户端负载均衡是保障系统高效、稳定运行的关键技术。通过 Ribbon 以及新一代的 Spring Cloud LoadBalancer,我们能够灵活地实现负载均衡策略,提高系统的可用性和性能。在实际项目中,我们需要根据业务场景和需求,合理选择和配置负载均衡策略,充分发挥 Spring Cloud 的优势,打造出健壮、高性能的互联网软件系统。希望本文对各位互联网软件开发人员在理解和应用 Spring Cloud 客户端负载均衡方面有所帮助,让我们一起在技术的道路上不断探索和前行。