概叙

负载均衡

负载均衡的两个基本点

  • 选择哪个服务器来处理客户端请求。
  • 将客户端请求转发出去。

一个核心原理:通过硬件或软件的方式维护一个服务列表清单。当用户发送请求时,会将请求发送给负载均衡器,然后根据负载均衡算法从可用的服务列表中选出一台服务器的地址,将请求进行转发,完成负载功能。

科普文:深入理解负载均衡(四层负载均衡、七层负载均衡)-CSDN博客

负载均衡的特性

高性能:可根据不同的分配规则自动将流量进行分摊。

可扩展性:可以很方便增加集群中设备或链路的数量。

高可靠性:系统中某个设备或链路发生故障,不会导致服务中断。

易配置性:配置和维护方便。

透明性:用户感知不到如何进行负载均衡的,也不用关心负载均衡。

负载均衡策略

我们来看下这些负载均衡器如何通过负载均衡策略来选择服务器处理客户端请求。

常见的均衡策略如下4种,其他还有

  • 最少连接数Least Connection、
  • 源 IP 哈希Source IP Hash、
  • 最少连接数慢启动时间Least Connection Slow Start Time、
  • 加权最少连接Weighted Least Connection、
  • 固定权重Fixed Weighted、
  • 加权响应Weighted Response等等就不再介绍。

1 轮循均衡(Round Robin)

原理:如果给服务器从 0 到 N 编号,轮询均衡策略会从 0 开始依次选择一个服务器作为处理本次请求的服务器。-

场景:适合所有父亲都有相同的软硬件配置,且请求频率相对平衡。

2 权重轮询均衡(Weighted Round Robin)

原理:按照服务器的不同处理能力,给服务器分配不同的权重,然后请求会按照权重分配给不同的服务器。

场景:服务器的性能不同,充分利用高性能的服务器,同时也能照顾到低性能的服务器。

3 随机均衡(Random)

原理:将请求随机分配给不同的服务器。

场景:适合客户端请求的频率比较随机的场景。

4 响应速度均衡(Response Time)

原理:负载均衡设备对每个服务器发送一个探测请求,看看哪台服务器的响应速度更快,

场景:适合服务器的响应性能不断变化的场景。

注意:响应速度是针对负载均衡设备和服务器之间的。

负载均衡分类

负载均衡技术可以按照软件或硬件进行分类,也可以按照服务器列表存放的位置划分为服务端负载和客户端负载均衡。

1 硬件负载均衡

F5 就是常见的硬件负载均衡产品。

优点:性能稳定,具备很多软件负载均衡不具备的功能,如应用交换,会话交换、状态监控等。

缺点:设备价格昂贵、配置冗余,没有软件负载均衡灵活,不能满足定制化需求。

2 软件负载均衡

Nginx:性能好,可以负载超过 1W。工作在网络的7层之上,可以针对http应用做一些分流的策略。Nginx也可作为静态网页和图片服务器。Nginx仅能支持http、https和Email协议。

LVS(Linux Virtual Server):是一个虚拟服务器集群系统,采用 IP 地址均衡技术和内容请求分发技术实现负载均衡。接近硬件设备的网络吞吐和连接负载能力。抗负载能力强、是工作在网络4层之上仅作分发之用。自身有完整的双机热备方案,如LVS+Keepalived。软件本身不支持正则表达式处理,不能做动静分离。

3 服务端负载均衡

Nginx 和 F5 都可以划分到服务端的负载均衡里面,后端的服务器地址列表是存储在后端服务器中或者存在专门的 Nginx 服务器或 F5 上。

服务器的地址列表的来源是通过注册中心或者手动配置的方式来的。

4 客户端负载均衡

终于轮到 Ribbon 登场了,它属于客户端负载均衡器,客户端自己维护一份服务器的地址列表。这个维护的工作就是由 Ribbon 来干的。

Ribbon 会从 Eureka Server 读取服务信息列表,存储在 Ribbon 中。如果服务器宕机了,Ribbon 会从列表剔除宕机的服务器信息。

Ribbon 有多种负载均衡算法,我们可以自行设定规则从而请求到指定的服务器。

OpenFeign底层默认负载均衡器Ribbon:RoundRobinRule

OpenFeign 底层使用的是 Ribbon 做负载均衡的,查看源码我们可以看到它默认的负载均衡策略是轮询策略:

然而除了轮询策略之外,我们还有其他 6 种内置的负载均衡策略可以选择,这些负载均衡策略如下:

  1. RoundRobinRule(轮询策略,按照服务顺序依次循环调用,默认负载均衡策略)

  2. 权重策略:WeightedResponseTimeRule,根据每个服务提供者的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性也就越低。它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。
  3. 最小连接数策略:BestAvailableRule,也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取。
  4. 区域敏感策略:ZoneAvoidanceRule,根据服务所在区域(zone)的性能和服务的可用性来选择服务实例,在没有区域的环境下,该策略和轮询策略类似。
  5. 可用敏感性策略:AvailabilityFilteringRule,先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例。
  6. 随机策略:RandomRule,从服务提供者的列表中随机选择一个服务实例。
  7. 重试策略:RetryRule,按照轮询策略来获取服务,如果获取的服务实例为 null 或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回 null。

出于性能方面的考虑,我们可以选择用权重策略或区域敏感策略来替代轮询策略,因为这样的执行效率最高。

ribbon配置服务策略

全局策略设置

使用以下方式配置的策略表示对该项目中调用的所有服务生效。

@Configuration
public class MyConfiguration{
    @Bean
    public IRule ribbonRule(){
        return new RandomRule();
    }
    
    //定义一个负载均衡的RestTemplate
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }

}

上面的配置表示:

  1. 定义了一个随机方式的服务调用方式,即随即调用某个服务的提供者;
  2. 定义一个负载均衡的 RestTemplate,使用了 @LoadBalanced注解,该注解配合覆盖均衡策略一起使用 RestTemplate 发出的请求才能生效。

RestTemplate是 Spring 提供的用于访问Rest服务的客户端模板工具集,Ribbon并没有创建新轮子,基于此通过负载均衡配置发出HTTP请求。

局部策略设置

如果在项目中你想对某些服务使用指定的负载均衡策略,那么可以如下配置:

@Configuration
@RibbonClients({
        @RibbonClient(name = "user-service",configuration = UserServiceConfig.class),
    	@RibbonClient(name = "order-service",configuration = OrderServiceConfig.class)
})
public class RibbonConfig {
}

@RibbonClients 中可以包含多个@RibbonClient。每个@RibbonClient表示一个服务名,后面对应的类表示该服务配套的策略规则。

如果你只想对一个服务应用某种规则,那么可以省略:@RibbonClients:

@Configuration
@RibbonClient(name = "order-service",configuration = OrderServiceConfig.class)
public class RibbonConfig {
}

超时重试

HTTP请求不免的会有网络不好的情况出现超时,Ribbon提供了超时重试机制,提供如下参数可以设置:

饥饿加载

Ribbon在进行客户端负载均衡的时候并不是在启动的时候就加载上下文的,实在实际请求的时候才加载,有点像servlet的第一次请求的时候才去生成实例,这会导致第一次请求会比较的缓慢,甚至可能会出现超时的情况。所以我们可以指定具体的客户端名称来开启饥饿加载,即在启动的时候便加载素养的配置项的应用上下文。

来一个本地全量配置:nacos+ribbon+openFeign

Ribbon负载均衡原理

Ribbon作为客户端负载均衡的工作原理可以概括为以下几个步骤:

参考:6000字 | 深入理解 Ribbon 的架构原理(文末送会员)-腾讯云开发者社区-腾讯云

Ribbon工作原理源码流程

  1. 初始化:Ribbon启动时,会加载配置的服务列表。

    依旧是以前的逻辑找自动装配类,进入spring-cloud-starer-loadbalnecer:2.2.6.REALEAS,查找spring.factories,发现里面并没有对应spring.factories,这说明的这个starter只是起到jar管理的作用(查看的mybatis和SpringBoot整合的源码的话,会发现也是这样),所以进入pom中会发现应该是在spring-cloud-loadbalancer里面。

    分析这里自动配置类BlockingLoadBalancerClientAutoConfiguration和刚才分析的BlockingLoadBalancerClient前边名称一样,那这个应该是重点分析的自动配置类

    进入BlockingLoadBalancerClientAutoConfiguration,会发现这里和Ribbon中的配置相似,都是在LoadBalancerAutoConfiguration之前。

  2. 负载均衡:Ribbon会根据预定的规则(如轮询、随机或最少活跃连接数)选择一个服务实例。

    RestTemplate发送请求一定经过LoadBalancerInterceptor,中的intercept方法,这里loadBalancer是BlockingLoadBalancerClient

    获取服务列表选择服务完成调用
  3. 服务调用:Ribbon将请求发送到选定的服务实例。

  4. 服务调用结果:原路返回。

上图可以体现出客户端负载均衡:即应用程序通过Ribbon从注册中心同步服务列表,然后在客户端通过配置的负载均衡策略IRrule进行服务调用。

Ribbon工作原理源码流程完善流程图

看网友的图 更直接。

Ribbon示例

一个 Eureka Server,3个Eureka Client,一个集成了Ribbon 的Consumer。

简单说一下关于 Ribbon consumer的配置:

pom文件中需要引入关于Ribbon的包,同时consumer也是一个Eureka Client要去拉 Eureka Server的配置,所以需要Eureka client的包。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

在启动类中初始化了两个bean:

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class RibbonDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(RibbonDemoApplication.class, args);
    }

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule ribbonRule() {
        return new RandomRule();//这里配置策略,和配置文件对应
    }
}

RestTemplate 和 IRule负载均衡策略。

然后就可以使用已经配置了负载均衡的 RestTemplate 发起请求了:

@Service
public class DemoService {

    @Autowired
    RestTemplate restTemplate;

    public String hello(String name) {

        return restTemplate.getForEntity("http://eureka-client/hello/" + name, String.class).getBody();
    }
}

Ribbon其他机制

服务端负载均衡的代表性例子就是nginx,LVS。那么客户端的负载均衡就是我们要说的Ribbon。Ribbon主要提供客户端负载平衡算法,除此之外,Ribbon还提供:

  • 服务发现集成 :功能区负载平衡器在动态环境(如云)中提供服务发现。功能区库中包含与Eureka和Netflix服务发现组件的集成;
  • 容错 : Ribbon API可以动态确定服务器是否已在实时环境中启动并运行,并且可以检测到那些已关闭的服务器;
  • 可配置的负载平衡规则 : Ribbon支持开箱即用的RoundRobinRuleAvailabilityFilteringRuleWeightedResponseTimeRule,还支持定义自定义规则。

Ribbon核心组件

Ribbon API提供以下组件供我们使用:

  1. Rule :定义负载均衡策略;Ribbon 的负载均衡策略和之前讲过的负载均衡策略有部分相同。7中策略详见上一章节。
  2. Ping : 定义如何ping目标服务实例来判断是否存活, ribbon使用单独的线程每隔一段时间(默认10s)对本地缓存的ServerList做一次检查;实现类主要有这几个:PingUrl、PingConstant、NoOpPing、DummyPing、NIWSDiscoveryPing。心跳检测策略对象 IPingStrategy,默认实现是轮询检测。
  3. ServerList :定义如何获取服务实例列表. 两种实现基于配置的ConfigurationBasedServerList和基于Eureka服务发现的DiscoveryEnabledNIWSServerList
    1. ServerList 主要用来获取所有服务的地址信息,并存到本地。

    2. 根据获取服务信息的方式不同,又分为静态存储和动态存储。

    3. 静态存储:从配置文件中获取服务节点列表并存储到本地。

    4. 动态存储:从注册中心获取服务节点列表并存储到本地

  4. ServerListFilter: 用来使用期望的特征过滤静态配置动态获得的候选服务实例列表. 若未提供, 默认使用ZoneAffinityServerListFilter
    1. 通过 Eureka 的分区规则对服务实例进行过滤。
    2. 比较服务实例的通信失败数和并发连接数来剔除不够健康的实例。
    3. 根据所属区域过滤出同区域的服务实例。
  5. ILoadBalancer: 定义了软负载均衡器的操作的接口. 一个典型的负载均衡器至少需要一组用来做负载均衡的服务实例, 一个标记某个服务实例不在旋转中的方法, 和对应的方法调用从实例列表中选出某一个服务实例;用于管理负载均衡的组件。初始化的时候通过加载 YMAL 配置文件创建出来的。
  6. ServerListUpdater: DynamicServerListLoadBalancer用来更新实例列表的策略(推EurekaNotificationServerListUpdater/拉PollingServerListUpdater, 默认是拉)

    服务列表更新就是 Ribbon 会从注册中心获取最新的注册表信息。是由这个接口 ServerListUpdater 定义的更新操作。而它有两个实现类,也就是有两种更新方式:

    1. 通过定时任务进行更新。由这个实现类 PollingServerListUpdater 做到的。
    2. 利用 Eureka 的事件监听器来更新。由这个实现类 EurekaNotificationServerListUpdater 做到的。
  7. RibbonClientConfiguration :RibbonClientConfiguration 类中通过 LoadBalancer,我们知道 ribbon 是靠LoadBalancer 做负载的 无非就是 ILoadBalancer 接口的方法,依次是添加新的服务、在负载均衡里选择一个服务、markServerDown 服务下线、获取服务列表、获取存活的服务器、获取所有服务器(包括健康和不健康的)
  8.  ZoneAwareLoadBalancer:loadBalancer 默认的是 ZoneAwareLoadBalancer 负载均衡器,通过继承父类DynamicServerListLoadBalancer 的 restOfInit 方法,里面比较重要的两个方法,enableAndInitLearnNewServersFeature和updateListOfServers 方法

参考:深入理解Spring Cloud Feign与Ribbon:优雅的微服务调用解决方案_springcloud集成ribbon实现服务列表刷新-CSDN博客

Spring Cloud新版本使用LoadBalancer替代Ribbon:探索与实践

在软件开发领域,负载均衡器是一种重要的技术组件,用于实现分布式系统中的流量分发和请求处理。

随着微服务架构的普及,负载均衡器的选择和使用变得尤为重要。

在Spring Cloud生态系统中,Ribbon和Spring Cloud LoadBalancer是两种常用的客户端负载均衡器。

然而,随着Ribbon的停更,Spring Cloud在Hoxton.M2版本中移除了Ribbon,并引入了Spring Cloud LoadBalancer作为替代品。

本文将介绍LoadBalancer的优势、与Ribbon的差异,以及在实际应用中的使用方法和建议。

一、LoadBalancer的优势
与Ribbon相比,Spring Cloud LoadBalancer具有以下优势:

  1. 支持响应式编程:LoadBalancer采用Spring WebFlux框架实现客户端负载均衡调用,支持响应式编程方式异步访问客户端,能够更好地应对高并发场景。
  2. 统一的配置管理:LoadBalancer作为Spring Cloud官方提供的客户端负载均衡器,可以与Spring Cloud其他组件无缝集成,实现统一的配置管理和监控。
  3. 更好的可扩展性:LoadBalancer提供了丰富的扩展点,开发者可以根据实际需求自定义负载均衡策略和过滤器,实现更加灵活和可扩展的负载均衡功能。
    二、LoadBalancer与Ribbon的差异
  4. 负载均衡算法:Ribbon提供了多种负载均衡算法,如轮询、随机等。而目前LoadBalancer的负载均衡算法相对较少,但未来可能会持续增加。
  5. 功能丰富度:Ribbon作为商业化产品,拥有丰富的功能和特性,如超时控制、重试机制等。相比之下,目前LoadBalancer的功能相对较少,但随着其不断发展,未来可能会增加更多功能。
  6. 社区支持:Ribbon作为Netflix开源项目,拥有庞大的社区支持和活跃的开发者群体。而目前LoadBalancer的社区相对较小,但随着其广泛应用,相信社区会逐渐壮大。
    三、使用LoadBalancer的建议
  7. 评估负载均衡需求:在使用LoadBalancer之前,建议开发者充分评估系统的负载均衡需求,包括流量分发策略、健康检查、容错处理等。
  8. 自定义负载均衡策略:根据实际需求,可以自定义负载均衡策略和过滤器,实现更加灵活和可扩展的负载均衡功能。例如,可以根据服务调用成功率或响应时间等指标动态调整负载均衡策略。
  9. 监控和日志记录:为了更好地了解系统运行状况和问题排查,建议对LoadBalancer进行监控和日志记录。可以使用Spring Cloud的监控组件如Micrometer和Zipkin等来实现对LoadBalancer的性能指标和调用链路的监控。同时,开启LoadBalancer的日志记录功能,以便于问题排查和调试。
  10. 兼容性考虑:由于目前LoadBalancer的功能相对较少,因此在某些场景下可能需要考虑与Ribbon或其他负载均衡器的兼容性。如果现有系统已经使用了Ribbon或其他负载均衡器,并且对其功能有较高的依赖性,那么在迁移到LoadBalancer时需要充分评估和测试系统的兼容性。
    总之,随着Ribbon的停更,Spring Cloud LoadBalancer成为Ribbon的一个可行的替代方案。虽然目前其功能和特性相对较少,但随着其不断发展,相信未来会提供更加丰富和灵活的负载均衡功能。在使用LoadBalancer时,建议充分评估系统的负载均衡需求,根据实际需求自定义负载均衡策略和过滤器,并加强监控和日志记录以保障系统的稳定性和可靠性。

Nacos 2021 不再集成 Ribbon,建议使用spring cloud loadbalancer
引入。

一、简单使用

引入依赖spring cloud loadbalancer

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
RestTemplate集成

启动类的restTemplate bean方法添加@LoadBalanced注解

@Bean
@LoadBalanced
public RestTemplate restTemplate(){
    return new RestTemplate();
}


二、修改默认负载均衡方式

Spring Cloud Balancer中实现了轮询RoundRobinLoadBalancer和随机数RandomLoadBalancer两种负载均衡算法

默认情况下的负载均衡为轮询RoundRobinLoadBalancer

如果我们需要改成随机RandomLoadBalancer,可以自定义

新建文件 CustomLoadBalancerConfiguration.java

package com.itmuch.contentcenter.rule;
 
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
 
@Configuration
public class CustomLoadBalancerConfiguration {
 
    @Bean
    ReactorLoadBalancer<ServiceInstance> loadBalancer(Environment environment,
                                                      LoadBalancerClientFactory loadBalancerClientFactory, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
 
 
        //轮询加载,默认就是这个
        /*return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,
                ServiceInstanceListSupplier.class),name);*/
 
        //返回随机轮询负载均衡方式
        return new RandomLoadBalancer(loadBalancerClientFactory.
                getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
 
        //nacos的负载均衡策略,按权重分配
        /*return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,
                ServiceInstanceListSupplier.class),
                name, nacosDiscoveryProperties);*/
    }
}
 
然后在启动类加注解@LoadBalancerClient,参数name为spring.application.name,configuration为上面自定义的类

package com.itmuch.contentcenter;
 
import com.itmuch.contentcenter.rule.CustomWeightLoadBalancerConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.spring.annotation.MapperScan;
 
// 扫描mybatis哪些包里面的接口
@MapperScan("com.itmuch")
@SpringBootApplication
@LoadBalancerClient(name = "user-center",configuration = CustomLoadBalancerConfiguration.class)
public class ContentCenterApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(ContentCenterApplication.class, args);
    }
 
    //在spring容器装载哪一个对象,类型为RestTemplate 名称为restTemplate
    // <bean id="restTemplate" clase ="xxx.xxx.RestTemplate"
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}


三、自定义负载均衡策略

如果我们想自定义策略。可以参考RoundRobinLoadBalancer类自己实现

以下为调3次轮换的自定义策略

CustomWeightLoadBalancerConfiguration.java

package com.itmuch.contentcenter.rule;
 
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
 
@Configuration
public class CustomWeightLoadBalancerConfiguration {
 
    @Bean
    ReactorLoadBalancer<ServiceInstance> weightloadBalancer(Environment environment,
                                                            LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
 
        //返回自定义负载均衡方式
        return new WeightLoadBalancer(loadBalancerClientFactory.
                getLazyProvider(name, ServiceInstanceListSupplier.class),
                name);
 
    }
}
 
WeightLoadBalancer.java

核心在于重载实现ReactorServiceInstanceLoadBalancer类的choose方法

package com.itmuch.contentcenter.rule;
 
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import reactor.core.publisher.Mono;
 
import java.util.List;
 
public class WeightLoadBalancer implements ReactorServiceInstanceLoadBalancer {
    private static final Log log = LogFactory.getLog(WeightLoadBalancer.class);
    private int total = 0;    // 被调用的次数
    private int index = 0;    // 当前是谁在提供服务
 
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private String serviceId;
 
    public WeightLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.serviceId = serviceId;
    }
 
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider.getIfAvailable();
        return supplier.get().next().map(this::getInstanceResponse);
    }
 
    //每个服务访问3次,然后换下一个服务
    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {
        log.info("进入自定义负载均衡");
        if (instances.isEmpty()) {
            return new EmptyResponse();
        }
 
        log.info("每个服务访问3次后轮询");
        int size = instances.size();
 
        ServiceInstance serviceInstance = null;
        while (serviceInstance == null) {
            System.out.println("===");
            if (total < 3) {
                serviceInstance = instances.get(index);
                total++;
            } else {
                total = 0;
                index++;
                if (index >= size) {
                    index = 0;
                }
                serviceInstance = instances.get(index);
            }
        }
        return new DefaultResponse(serviceInstance);
    }
 
 
}

最后跟上面一样,启动类加注解@LoadBalancerClient,指向自定义的config

@LoadBalancerClient(name = "user-center",configuration = CustomWeightLoadBalancerConfiguration.class)

四、使用nacos的负载均衡策略

在nacos定义了负载均衡策略类NacosLoadBalancer,我们可以直接使用

使用配置类方式

package com.itmuch.contentcenter.rule;
 
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
 
@Configuration
public class CustomLoadBalancerConfiguration {
 
    @Bean
    ReactorLoadBalancer<ServiceInstance> loadBalancer(Environment environment,
                                                      LoadBalancerClientFactory loadBalancerClientFactory, NacosDiscoveryProperties nacosDiscoveryProperties) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
 
 
        //nacos的负载均衡策略,按权重分配
        return new NacosLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,
                ServiceInstanceListSupplier.class),
                name, nacosDiscoveryProperties);
    }
}
 
然后还是启动类加注解@LoadBalancerClient,指向自定义的config

@LoadBalancerClient(name = "user-center",configuration = CustomLoadBalancerConfiguration.class)
配置文件方式

#开启nacos的负载均衡策略
spring.cloud.loadbalancer.nacos.enabled=true
如果需要根据nacos的权重进一步自定义,可参考NacosLoadBalancer的代码自己实现

可参考:Nacos负载均衡策略_nacos负载均衡策略配置-CSDN博客

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐