玩转SpringCloud专题(九)-SpringCloud之Ribbon负载均衡

我只是一个虾纸丫 提交于 2020-03-11 10:04:09

1.Ribbon负载均衡简介

1.1Ribbon概述

1.1.1.Ribbon是什么

SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。

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

1.1.2.Ribbon主要职责

LB(负载均衡)

LB,即负载均衡( Load Balanoe ),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA。
常见的负载均衡有软件nginx , LVS,硬件F5等。
相应的在中间件,例如:dubbo和 SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。

LB又分为两种,集中式LB和进程内LB

集中式LB(偏硬件)

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

进程内LB(偏软件)

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

在这里插入图片描述

1.1.3 官方资料

https://github.com/Netflix/ribbon/wiki

2.Ribbon实例

上一篇的案例中,我们启动了一个springcloud-demo,然后通过DiscoveryClient来获取服务实例信息,然后获取ip和端口来访问。

但是实际环境中,我们往往会开启很多个user-service的集群。此时我们获取的服务列表中就会有多个,到底该访问哪一个呢?

一般这种情况下我们就需要编写负载均衡算法,在多个实例列表中进行选择。不过Eureka中已经帮我们集成了负载均衡组件:Ribbon,简单修改代码即可使用。

在这里插入图片描述
接下来,我们就来使用Ribbon实现负载均衡。

2.1.Ribbon架构说明

在这里插入图片描述
Ribbon 在工作时分成两步:

第一步先选择 EurekaServer,它优先选择在同一个区域内负载较少的server。
第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。

其中Ribbon 提供了多种策略:比如轮询、随机和根据响应时间加权。

2.2.启动两个服务实例

首先我们启动两个springcloud-demo实例,一个80,一个81。
在这里插入图片描述
Eureka监控面板:
在这里插入图片描述

2.3.开启负载均衡

因为Eureka中已经集成了Ribbon,所以我们无需引入新的依赖

加入Ribbon的配置

# EurekaServer地址
eureka.client.service-url.defaultZone=http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

在RestTemplate的配置方法上添加@LoadBalanced注解

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

修改调用方式,不再手动获取ip和端口,而是直接通过服务名称调用

/**
 * @author bruceliu
 * @create 2019-05-02 15:52
 * @description
 */
@RestController
@RequestMapping("consumer")
public class ConsumerController {

    //改成下面这行
    private static final String REST_URL_PREFIX = "http://SPRINGCLOUD-DEMO-SERVICE";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping("/test")
    public List<User> consumerTest(){

        return this.restTemplate.getForObject(REST_URL_PREFIX+"/all",List.class);
    }
}

测试
在这里插入图片描述
在这里插入图片描述
Ribbon整合后可以直接调用微服务而不再关心地址和端口!!!

2.4.源码跟踪

为什么我们只输入了service名称就可以访问了呢?之前还要获取ip和端口。显然有人帮我们根据service名称,获取到了服务实例的ip和端口。它就是LoadBalancerInterceptor
我们进行源码跟踪:
在这里插入图片描述
继续跟入execute方法:发现获取了8001端口的服务

org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient

在这里插入图片描述
如果再跟下一次,发现获取的是8002

2.5.负载均衡策略

Ribbon默认的负载均衡策略是简单的轮询,我们可以测试一下:
编写测试类,在刚才的源码中我们看到拦截中是使用RibbonLoadBalanceClient来进行负载均衡的,其中有一个choose方法,是这样介绍的:
在这里插入图片描述
现在这个就是负载均衡获取实例的方法。我们对注入这个类的对象,然后对其测试:

package com.bruceliu.test;

import com.bruceliu.SpringcloudDemoConsumerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author bruceliu
 * @create 2019-05-04 11:33
 * @description 负载均衡算法测试
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SpringcloudDemoConsumerApplication.class)
public class LoadBalanceTest {

    @Autowired
    RibbonLoadBalancerClient client;

    @Test
    public void test1(){
        for (int i = 0; i <10 ; i++) {
            ServiceInstance instance = this.client.choose("SPRINGCLOUD-DEMO-SERVICE");
            System.out.println(instance.getHost() + ":" + instance.getPort());
        }
    }
}

运行结果:
在这里插入图片描述
符合了我们的预期推测,确实是轮询方式

我们是否可以修改负载均衡的策略呢?继续跟踪源码,发现这么一段代码:
在这里插入图片描述
我们看看这个rule是谁:
在这里插入图片描述
这里的rule默认值是一个RoundRobinRule,看类的介绍:
在这里插入图片描述
这不就是轮询的意思嘛。

我们注意到,这个类其实是实现了接口IRule的,查看一下:
在这里插入图片描述
定义负载均衡的规则接口。
它有以下实现:
在这里插入图片描述
SpringBoot也帮我们提供了修改负载均衡规则的配置入口:

SPRINGCLOUD-DEMO-SERVICE:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

格式是:{服务名称}.ribbon.NFLoadBalancerRuleClassName,值就是IRule的实现类。
再次测试,发现结果变成了随机:
在这里插入图片描述

2.6.重试机制

Eureka的服务治理强调了CAP原则中的AP,即可用性和可靠性。它与Zookeeper这一类强调CP(一致性,可靠性)的服务治理框架最大的区别在于:Eureka为了实现更高的服务可用性,牺牲了一定的一致性,极端情况下它宁愿接收故障实例也不愿丢掉健康实例,正如我们上面所说的自我保护机制。

但是,此时如果我们调用了这些不正常的服务,调用就会失败,从而导致其它服务不能正常工作!这显然不是我们愿意看到的。

我们现在关闭一个springcloud-demo-8001实例在这里插入图片描述
因为服务剔除的延迟,consumer并不会立即得到最新的服务列表,此时再次访问你会得到错误提示:
在这里插入图片描述
但是此时,8002服务其实是正常的。

因此Spring Cloud 整合了Spring Retry 来增强RestTemplate的重试能力,当一次服务调用失败后,不会立即抛出一次,而是再次重试另一个服务。
只需要简单配置即可实现Ribbon的重试:

spring:
  cloud:
    loadbalancer:
      retry:
        enabled: true # 开启Spring Cloud的重试功能
SPRINGCLOUD-DEMO-SERVICE:
  ribbon:
    ConnectTimeout: 250 # Ribbon的连接超时时间
    ReadTimeout: 1000 # Ribbon的数据读取超时时间
    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试
    MaxAutoRetriesNextServer: 1 # 切换实例的重试次数
    MaxAutoRetries: 1 # 对当前实例的重试次数

根据如上配置,当访问到某个服务超时后,它会再次尝试访问下一个服务实例,如果不行就再换一个实例,如果不行,则返回失败。切换次数取决于MaxAutoRetriesNextServer参数的值

引入spring-retry依赖

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

我们重启springcloud-demo-consumer,测试,发现即使springcloud-demo-8001宕机,也能通过另一台服务实例获取到结果!

2.7 Ribbon核心组件IRule(面试题)

策略名称 策略对应的类名 实现原理
轮询策略(默认) RoundRobinRule 轮询策略表示每次都顺序取下一个 provider,比如一共有 5 个provider,第 1 次取第 1 个,第 2次取第 2 个,第 3 次取第 3 个,以此类推
权重轮询策略 WeightedResponseTimeRule 1.根据每个 provider 的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。2.原理:一开始为轮询策略,并开启一个计时器,每 30 秒收集一次每个 provider 的平均响应时间,当信息足够时,每个 provider附上一个权重,并按权重随机选择provider,高权越重的 provider会被高概率选中。
随机策略 RandomRule 从 provider 列表中随机选择一个provider
最少并发数策略 BestAvailableRule 选择正在请求中的并发数最小的 provider,除非这个provider 在熔断中。
在“选定的负载均衡策略”基础上进行重试机制 RetryRule 1.“选定的负载均衡策略”这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择 provider 不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider
可用性敏感策略 AvailabilityFilteringRule 过滤性能差的 provider,有 2种:第一种:过滤掉在 eureka 中处于一直连接失败 provider 第二种:过滤掉高并发的 provider
区域敏感性策略 ZoneAvoidanceRule 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider2.如果这个 ip 区域内有一个或多个实例不可达或响应变慢,都会降低该 ip 区域内其他 ip 被选中的权重。

2.8 修改访问服务的算法方式

方式一:修改代码更换负载均衡策略

在启动类中添加实例负载均衡的实例,则默认的轮询策略就会失效,具体如下:

@EnableEurekaClient
@SpringBootApplication
public class SpringcloudEurekaConsumerApplication {
    /**
     * 显示实例化 负载均衡的策略对象,那么默认的轮询策略就会失效
     * @return
     */
    @Bean
    public RandomRule createRule(){
        return new RandomRule();
    }

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

方式二:修改配置文件更换负载均衡策略
第二种方式在application.properties中设置分配的策略

#设置负载均衡策略 eureka-ribbon-provider 为调用的服务的名称
eureka-ribbon-provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule

'eureka-ribbon-provider’是调用的服务的名称,后面的组成部分是固定的。
同时注释掉方式一中的内容

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!