1、负载均衡器Ribbon
1.1、什么是ribbon
什么是负载均衡:负载均衡就是当一项服务部署到了多台机器的时候,请求合理均衡的分配到所有的机器上处理,就是负载均衡
主流的负载方案有两种:
- 服务端负载均衡,在消费者和服务提供方中间使用独立的代理方式实现负载均衡,也就是使用中间件。有硬件的(f5),也有软件的(nginx)
- 客户端根据自己的请求情况做负载均衡,ribbon就是属于客户端自己做负载均衡。也就是说,nacos将所有的服务地址给我们,我们自己均衡的去选择访问哪一项
spring-cloud-ribbon是基于netflix ribbon实现的一套客户端负载均衡工具,ribbon客户端组件提供了一系列的完善的配置,比如超时,重试等等。通过load Balancer获取到服务提供的所有机器实例,ribbon会基于某种规则(轮询,随机,权重)去调用服务。我们也可以自定义实现我们自己的负载均衡策略
1.1.1.客户端负载均衡
客户端根据自己的请求情况做负载均衡,ribbon就是属于客户端自己做负载均衡。也就是说,nacos将所有的服务地址给我们,我们自己均衡的去选择访问哪一项
1.1.2.服务端负载均衡
服务端负载均衡,在消费者和服务提供方中间使用独立的代理方式实现负载均衡,也就是使用中间件。有硬件的(f5),也有软件的(nginx)
响应的时候先经过中间件,中间件选一个发送给调用者
1.1.3.常见负载均衡算法
- 随机,通过随机选择服务执行
- 轮询,负载均衡默认的实现方法,请求一个一个轮流处理
- 加权轮询,通过对服务器性能分析,给高配低负载的服务器更高的权重也就是给他处理更多的请求,均衡压力
- 地址Hash,通过客户端请求的地址的hash值取模进行服务器调度
- 最小链路数,及时请求均衡了,压力也不一定均衡,最小连接数法就是根据服务器的情况,比如请求积压数等参数,将请求分配到当前压力最小的服务器上,最小活跃数
1.2、nacos使用ribbon
由于nacos-discover已经有ribbon的依赖,我们不需要添加任何依赖
在SpringCloud第一代中使用Ribbon、SpringCloud第二代中直接采用自研发loadbalance即可,默认使用的Ribbon
1.2.1.使用LoadBalancerClient
1.注入LoadBalancerClient
@Autowired
private LoadBalancerClient loadBalancerClient;
2.调用loadBalancerClient
@GetMapping("/ribbon")
public URI ribbon(){
// 通过地址自动负载均衡
ServiceInstance choose = loadBalancerClient.choose("provider");
return choose.getUri();
}
1.2.2.使用@LoadBalanced
注解配合restTemplate实现
1.配置bean
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
2.url使用服务名替换路径调用
我们这里服务名叫provider,所以为http://provider/hello,我们访问hello接口
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(){
//进行了拼接地址加
String forObject = restTemplate.getForObject("http://provider/hello/", String.class);
return "我是消费者拿生产者:"+forObject;
}
1.3、ribbon配置
1.3.1.ribbon的负载均衡策略
IRule
这是所有负载均衡策略的父接口,里面的核心方法是choose方法,用来选择一个服务实例
AbstractLoadBalancerRule
AbstractLoadBalancerRule是一个抽象类,里面主要定义了一个ILoadBalancer,就是我们上文所说的负载均衡器,这里定义他主要是为了辅助负责均衡策略选取核实的服务端实例
- RandomRule,该策略就是随机选择一个服务实例。在RandomRule的无参构造中初始化了一个Random对象,然后他重写的choose方法调用了
choose(ILoadBalancer ib, Object key)
这个重载的choose方法,在这个重载的choose方法中,每次利用random对象生成一个不大于服务实例总数的随机数,并将该随机数作为下标索引获取一个服务实例 - RoundRobinRule,该策略叫做线性轮询策略。这个类的choose(ILoadBalancer lb, Object key)函数整体逻辑是这样的︰开启一个计数器count,在while循环中遍历服务清单,获取清单之前先通过incrementAndGetModulo方法获取一个下标,这个下标是一个不断自增长的数先加1然后和服务清单总数取模之后获取到的(所以这个下标从来不会越界),拿着下标再去服务清单列表中取服务,每次循环计数器都会加1,如果连续10次都没有取到服务,则会报一个警告No available alive servers after 10 tries from load balancer: XXXX
- **RetryRule(轮询基础上进行重试)**看名字就知道这种负载均衡策略带有重试功能。首先RetryRule中又定义了一个subRule,它的实现类是RoundRobinRule,然后在RetryRule的choose(LoadBalancerlb,Object key)方法中,每次还是采用RoundRobinRule中的choose规则来选择一个服务实例,如果选到的实例正常就返回,如果选择的服务实例为nul或者已经失效,则在失效时间deadine之前不断的进行重试(重试时获取服务的策略还是RoundRobinRule中定义的策略),如果超过了deadline还是没取到则会返回一个null
- **WeightedResponseTimeRule(权重-nacos的nacosRule,Nacos还拓展了一个自己的基于配置的权重拓展)**他对RoundRobinRule的功能进行了拓展,他会根据每一个实例的运行情况来给计算出该实例的一个权重,然后再挑选实例的时候根据权重进行挑选,这样能够实现更优的实例调用。他里面有一个叫DynamicServerWeightTask的定时任务,默认情况下每三十秒会计算一次各个服务实例的权重,权重的计算规则也很简单,如果一个服务的平均响应时间越短则权重越大,那么该服务实例被选中执行任务的概率也就越大
- **BestAvailableRule(过滤失效的服务实例,找出并发请求最小的实例来使用)**他继承ClientConfigEnabledRoundRobinRule,在他的基础上主要增加loadBalancerStats中保存的服务实例的状态信息来过滤失效的服务实例,找出并发请求最小的实例来使用。然而loadBalancerStats有可能为null,则BestAvaliableRule将采用他的父类ClientConfigEnabledRoundRobinRule的服务选取策略(线性轮询)
- **ZoneAvoidanceRule(默认规则,符合判断server所在区域的性能和server的可用性选择服务器)**他是PredicateBasedRule的一个实现类,只不过这里多一个过滤条件,他的过滤ZoneAvoidancePredicate为主过滤条件和以AvailabilityPredicate为次过滤条件组成的一个叫做CompositePredicate的组合过滤条件,过滤成功后,继续采用线性轮询(RoundRobinRule)的方式从过滤结果中选择一个出来
- **AvailabilityFilteringRule(先过滤故障实例,再选择并发较小的实例)**过滤掉一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的后端server或者使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就是检查status里记录的各个server的运行状态
1.3.2.修改默认负载均衡策略
修改默认负载均衡策略有两种方式,一种是通过配置bean的方式配置,一种是通过yaml配置文件的方式,这里我们更推荐使用yaml配置文件的方式
1.3.2.1.配置bean
注意:再配置bean之前我们需要注意一个问题,如果我们的配置类被自动扫描进spring容器中,那么全局的负载均衡都会使用这么一个策略,这不适合我们对不同的生产者使用不同的负载均衡策略。所以,我们的配置类必须不能被主类扫描到
1.我们要将这个类的实现装配到全局中
注意位置,不能被扫描到
@Configuration
public class RibbonConfig {
/**
* 全局配置
* 指定负载均衡策略,可以指定我们之前学习的所有策略
* @return
*/
@Bean
public IRule iRule(){
// 指定使用nacos提供的负载均衡策略(优先调用统一集群的实例,基于随机权重)
return new NacosRule();
}
}
2.利用@RibbonClient在主加载类指定微服务及其负载均衡策略
//name:服务提供者名称
//configuration:他的配置类的类文件
@RibbonClient(name = "provider",configuration = RibbonRandomRuleConfig.class)
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClients(value ={
// 指定负载均衡的服务提供者,以及负载均衡策略配置类的类对象
@RibbonClient(name = "provider",configuration = RibbonRandomRuleConfig.class)
})
public class Ribbon {
public static void main(String[] args) {
SpringApplication.run(Ribbon.class, args);
}
}
1.3.2.2.配置文件
我们现在演示配置文件的方式配置负载均衡策略
我们只需要修改配置文件即可,注意,这里我们是没有提示的,也就是说源码直接来读键值对,注意需要直接丢根下而不能空格
# properties配置文件
服务提供名.ribbon.NFLoadBalancerRuleClassName = 负载均衡策略全类名
例如:
provider.ribbon.NFLoadBalancerRuleClassName = com.alibaba.cloud.nacos.ribbon.NacosRule
--------------------------------
# yaml配置文件
服务提供名:
ribbon:
NFLoadBalancerRuleClassName : 负载均衡策略全类名
例如:
provider:
ribbon:
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule
这里我们的NacosRule是基于nacos的权重来实现的负载均衡
1.3.3.自定义负载均衡策略
我们自定义负载均衡策略非常简单,我们只需要实现父类接口(IRule)或者基类(AbstractLoadBalancerRule·)就行,使用方法同上
1.编写配置类
public class MyRibbonRule extends AbstractLoadBalancerRule {
@Override
public Server choose(Object key) {
//获取当前请求的服务实例
ILoadBalancer loadBalancer = this.getLoadBalancer();
//获取到实例列表
List<Server> servers = loadBalancer.getReachableServers();
//线程安全的随机获取一个下标
int i = ThreadLocalRandom.current().nextInt(servers.size());
return servers.get(i);
}
//初始化配置的,我们不学那么深
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {}
}
2.我们使用配置的方式注入进去
# 权重配置
provider:
ribbon:
NFLoadBalancerRuleClassName: com.hy.ribbon.MyRibbonRule
1.3.4.小问题
这里有个小问题,就是我们负载均衡策略是懒加载的,在消费端调用的时候,才会去请求nacos拿到列表并加载。那么我们第一次调用的时候可能会出现超时的情况。如果想要直接加载,我们需要做一定的配置
# 配置负载均衡策略直接加载
ribbon:
eager-load:
# 开启饥饿加载,即启动直接加载
enabled: true
# 配置需要直接加载的服务提供者名,多个使用英文逗号隔开
clients: provider
1.4、LoadBalancer
LoadBalancer是spring cloud官方自己提供的客户端负载均衡器,用来替代Ribbon
spring官方提供了两种负载均衡客户端:
RestTemplate
RestTemplate是Spring提供的用于访问rest服务的客户端,RestTemplate提供了多种便捷访问远程http服务的方法,能够大大的提高客户端的编写效率。默认情况下,RestTemplate依赖jdk的http连接工具
WebClient
WebClient是从Spring WebFlux 5.0版本开始提供的一个非阻塞的基于响应式编程的进行http请求的客户端工具
1.4.1、使用LoadBalancer
1.引入LoadBalancer依赖
<!-- 引入LoadBalancer依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2.排除ribbon
我们方才知道,虽然我们使用的是RestTemplate,但是他默认是使用ribbon的,我们要使用loadBalancer就需要移除ribbon
移除方式有两种,在pom依赖中排除,或者在配置文件yaml中声明不使用ribbon
- pom依赖排除,并引入LoadBalancer
<!--client发现依赖 排除ribbon依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
- yaml声明不使用ribbon
spring:
application:
# 服务名,如果nacos没有配置,则用他
name: consumer-with-ribbon
cloud:
nacos:
# 服务发现配置
discovery:
server-addr: 175.178.58.107:8848 # nacos发现地址
username: nacos # nacos用户名
password: LM647688lx # nacos密码
namespace: a048578c-ec50-4db1-bfd7-f8f69eb96622 # 工作空间(不加为默认)
group: my-group # # 配置分组(不加为默认)分在同一个组才能相互发现
service: consumer-with-ribbon # 服务名称
# nacos地址
server-addr: 175.178.58.107:8848
loadbalancer:
ribbon:
enabled: false
3.使用LoadBalancer
直接使用RestTemplate即可。原理就是原生使用的ribbon被排除了,并且LoadBalancer引入,所以我们使用他
@RestController
public class TestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(){
String forObject = restTemplate.getForObject("http://provider/hello/", String.class);
return "我是消费者拿生产者:"+forObject;
}
}
1.4.2、深入了解LoadBalancer
ribbon负载均衡提供了很多种均衡的方式,而LoadBalancer只提供了轮询的方式。spring官方提出他可能主要是为了提供一些接口,用来制造生态,就目前看来并没有什么好效果。所以等他火了再说吧。
当然LoadBalancer既然提供了接口,那么我们同样可以自定义负载均衡策略。这里我们就不深入了解了,提供一个官方文档看看
官方文档在spring-cloud-commons里面