【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现
  707fzl7bQVmn 2023年11月02日 32 0


负载均衡的实现方式

  • 客户端的负载均衡
  • 服务端的负载均衡(经常使用nginx来实现)

服务端负载均衡

  1. 接收请求
  2. 选择服务器地址
  3. 转发请求

客户端负载均衡

  1. 选择服务器地址
  2. 发请求

也可以分为集中式负载均衡进程内负载均衡

集中式负载均衡

即在服务的消费方和提供方之间使用独立的负载均衡设施(可以是硬件,如F5,可以是软件,如nginx),由该实施负责把访问请求通过某种策略转发至服务的提供方

进程内负载均衡

将负载均衡逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内负载均衡,它只是一个类库,集成于消费方进程,消费方通过它来获取服务提供方的地址

Ribbon的简单介绍

Spring Cloud Ribbon是基于Netfilx Ribbon实现一套客户端负载均衡的工具。简单的说,Ribbon是Netfilx 发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项,如:连接超时、重试等。
简单的说,就是在配置文件列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如:简单轮询、随机连接等)去连接这些机器。
我们很容易使用Ribbon实现自定义负载均衡算法。

Ribbon 注册到nacos server之后,Ribbon会从nacos server获取服务列表

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_服务器

Ribbon 负载均衡策略的简单介绍

IRule 接口的实现类定义了一系列负载规则

IRule的类图

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_spring_02


负载策略的大致功能实现

策略名

策略声明

策略描述

实现说明

BestAvailableRule

public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule

选择一个最小的并发请求的server

逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server

AvailabilityFilteringRule

public class AvailabilityFilteringRule extends PredicateBasedRule

过滤掉那些因为一直连接失败的被标记为circuit

tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态

WeightedResponseTimeRule

public class WeightedResponseTimeRule extends RoundRobinRule

根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。

一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成status时,使用roubine策略选择server。

RetryRule

public class RetryRule extends AbstractLoadBalancerRule

对选定的负载均衡策略机上重试机制。

在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server

RoundRobinRule

public class RoundRobinRule extends AbstractLoadBalancerRule

roundRobin方式轮询选择server

轮询index,选择index对应位置的server

RandomRule

public class RandomRule extends AbstractLoadBalancerRule

随机选择一个server

在index上随机,选择index对应位置的server

ZoneAvoidanceRule

public class ZoneAvoidanceRule extends PredicateBasedRule

复合判断server所在区域的性能和server的可用性选择server

使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。

ResponseTimeWeightedRule已经被弃用,作用和WeightedResponseTimeRule一样。

还是以​​Nacos的简单介绍以及服务注册与发现功能的基本使用​​这篇博客为基础,我们来探究一下Ribbon负载均衡的基本使用

示例

nacos-provider(提供服务)

基本项目结构如下

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_服务器_03


还是​​Nacos的简单介绍以及服务注册与发现功能的基本使用​​这篇博客的代码,可以去这篇博客的nacos-provider章节查看,我就不搬运过来了

nacos-consumer(消费服务)

基本结构如下

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_负载均衡_04


其中feign包是使用feign调用远程服务不用管它

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.xt</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>nacos-consumer</artifactId>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

</project>

application.yml配置文件

server:
port: 8085

spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: 部署nacos server的服务器IP:8848

feign:
hystrix:
enabled: true

RibbonConfig

写一个ribbon配置类
IRule 接口的实现类定义了一系列负载规则
如果自己不指定,默认使用的就是轮询算法。
先使用RandomRule的随机来做测试

@Configuration
public class RibbonConfig {

@Bean
public IRule iRule() {
// 权重负载策略 nacos的负载均衡实现
// return new NacosRule(); 是spring cloud alibaba 继承了netflix.loadbalancer包的AbstractLoadBalancerRule抽象类的实现

// 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
// return new BestAvailableRule();

// 复合判断server所在区域的性能和server的可用性选择服务器
// return new ZoneAvoidanceRule();

// 先过滤掉故障实例,再选择并发较小的实例
// return new AvailabilityFilteringRule();

// 对RoundRobinRule 轮询的扩展,响应速度越快的实例选择权重越多大,越容易被选择
// return new WeightedResponseTimeRule();

// return new ResponseTimeWeightedRule(); 已经被弃用

// 随机
return new RandomRule();

// 先按照RoundRobinRule 轮询的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
// return new RetryRule();

}
}

controller

@RestController
public class TestController {

private final RestTemplate restTemplate;

@Autowired
public TestController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}

@RequestMapping(value = "/echo-restemplate/{str}", method = RequestMethod.GET)
public String echo(@PathVariable String str) {
return restTemplate.getForObject("http://nacos-provider/echo/" + str, String.class);
}


@Autowired
EchoService echoService;

@RequestMapping(value = "/echo-feign/{str}",method = RequestMethod.GET)
public String feign(@PathVariable String str) {
return echoService.echo(str);
}


}

feign(远程调用服务的东西)

@Component
@FeignClient(name = "nacos-provider")
public interface EchoService {


@RequestMapping(value = "/echo-error/{str}",method = RequestMethod.GET)
String echo(@PathVariable("str") String str);

//fallback实现类
@Component
class EchoServiceFallback implements EchoService{
@Override
public String echo(@PathVariable("str") String str) {
return "接口请求失败";
}
}

}

MyLoadBalancer类(自定义负载均衡的实现,我这里写死了,只访问一个实例)

public class MyLoadBalancer extends AbstractLoadBalancerRule{


public MyLoadBalancer() {
}

@SuppressWarnings({"RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"})
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
} else {
Server server = null;

while(server == null) {
//测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。
if (Thread.interrupted()) {
return null;
}
//获取已启动且可访问的服务器。
List<Server> upList = lb.getReachableServers();
//获取所有已知的服务器(可访问和不可访问)
List<Server> allList = lb.getAllServers();
//全部服务的实例个数
int serverCount = allList.size();

if (serverCount == 0) {
return null;
}

//RandomRule使用ThreadLocalRandom获取随机数,我这里直接写死,我只要1实例提供服务
int index = 1;

server = (Server)upList.get(index);
if (server == null) {
//放出CPU资源
Thread.yield();
} else {
if (server.isAlive()) {
return server;
}

server = null;
Thread.yield();
}
}

return server;
}

}

protected int chooseRandomInt(int serverCount) {
//ThreadLocalRandom,在多线程下,它为每个线程维护一个 seed 变量
return ThreadLocalRandom.current().nextInt(serverCount);

}

@Override
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}

@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}

}

NacosConsumerApplication启动类

使用RestTemplate来请求服务,向Spring的IOC注入一个bean: restTemplate;并通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@RibbonClient(name="nacos-provider",configuration= RibbonConfig.class)
public class NacosConsumerApplication {

// @Bean
// @Scope(value = "prototype")
// public IRule loadBalanceRule() {
// return new NacosRule();
// }

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

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

}

像​​Nacos的简单介绍以及服务注册与发现功能的基本使用​​这篇博客一样,先开启三个nacos-provider服务。再开启一个nacos-consumer服务

登录服务器部署的nacos服务的控制台(默认账号和密码都是nacos)

服务器IP:8848/nacos

我们可以看到我们注册好的4个服务实例

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_spring_05

测试Ribbon自带的随机策略

配置类,设置成 RandomRule

return new RandomRule();

如下所示,将RandomRule注入到Spring 的IOC 容器中进行管理

@Configuration
public class RibbonConfig {

@Bean
public IRule iRule() {
// 权重负载策略 nacos的负载均衡实现
// return new NacosRule(); 是spring cloud alibaba 继承了netflix.loadbalancer包的AbstractLoadBalancerRule抽象类的实现

// 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
// return new BestAvailableRule();

// 复合判断server所在区域的性能和server的可用性选择服务器
// return new ZoneAvoidanceRule();

// 先过滤掉故障实例,再选择并发较小的实例
// return new AvailabilityFilteringRule();

// 对RoundRobinRule 轮询的扩展,响应速度越快的实例选择权重越多大,越容易被选择
// return new WeightedResponseTimeRule();

// return new ResponseTimeWeightedRule(); 已经被弃用

// 随机
return new RandomRule();

// 先按照RoundRobinRule 轮询的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
// return new RetryRule();

}

}

访问nacos-consumer服务,然后nacos-consumer服务访问请求nacos-provider服务,点击刷新多次

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_服务器_06


1号provider服务实例

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_负载均衡_07


2号provider服务实例

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_负载均衡_08


3号provider服务实例

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_spring_09


可以看出,确实是随机策略

测试自定义的负载均衡策略

修改RibbonConfig,向Spring IOC容器中注入MyLoadBalancer

return new MyLoadBalancer();
@Configuration
public class RibbonConfig {

@Bean
public IRule iRule() {
// 权重负载策略 nacos的负载均衡实现
// return new NacosRule(); 是spring cloud alibaba 继承了netflix.loadbalancer包的AbstractLoadBalancerRule抽象类的实现

// 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
// return new BestAvailableRule();

// 复合判断server所在区域的性能和server的可用性选择服务器
// return new ZoneAvoidanceRule();

// 先过滤掉故障实例,再选择并发较小的实例
// return new AvailabilityFilteringRule();

// 对RoundRobinRule 轮询的扩展,响应速度越快的实例选择权重越多大,越容易被选择
// return new WeightedResponseTimeRule();

// return new ResponseTimeWeightedRule(); 已经被弃用

// 随机
// return new RandomRule();

// 先按照RoundRobinRule 轮询的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
// return new RetryRule();

//自己的负载均衡策略,只访问可访问服务列表的下标为1的服务实例
return new MyLoadBalancer();

}

}

重新运行nacos-consumer服务实例

然后再次使用浏览器访问nacos-consumer服务,让他去消费nacos-provider服务。

【Spring Cloud】Ribbon负载均衡策略介绍以及自定义负载均衡实现_负载均衡_10


我多次点击刷新浏览器,只有3号provider服务实例的被调用次数在增加,说明我们的修改生效了。

注意:虽然我的负载均衡策略是设置的活动服务实例列表中的下标为1的服务实例接收消费。这里虽然是3号provider实例,但是下标为1是指列表里面的下标为1

//获取已启动且可访问的服务器。
List<Server> upList = lb.getReachableServers();

References:

(写博客主要是对自己学习的归纳整理,资料大部分来源于书籍、网络资料和自己的实践,整理不易,但是难免有不足之处,如有错误,请大家评论区批评指正。同时感谢广大博主和广大作者辛苦整理出来的资源和分享的知识。)


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  ehrZuhofWJiC   2024年05月17日   36   0   0 服务器linux
707fzl7bQVmn