SpringCloud--3.ribbon负载均衡

1.ribbon是什么?

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

2.负载均衡能干嘛?

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

  • 集中式LB
    在服务的消费方和提供方之间使用独立的LB设施,如Nginx(反向代理服务器), 由该设施负责把访问请求通过某种策略转发至服务的提供方!
  • 进程式LB
    将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选出一个合适的服务器。Ribbon就属于
    进程内LB
    ,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址!

3.实现一个ribbon demo

(1)demo还是之前文章的项目,由于这是ribbon是客户端需要的,所以我们在spring-cloud-consumer-dept项目下导入新依赖:


        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-ribbonartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-eurekaartifactId>
        dependency>

(2)新增配置文件信息

#eureka配置
eureka:
  client:
    register-with-eureka: false #不向注册中心注册自己,因为这是客户端
    service-url: #获取服务的地址
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/

(3)在启动类上添加@EnableEurekaClient注解,标识这是eureka客户端
(4)给之前ConfigBean下的bean加上@LoadBalanced注解,进行ribbon负载均衡
SpringCloud--3.ribbon负载均衡_第1张图片
(5)修改之前在controller下写死的url:REST_URL_PREFIX,因为我们要进行负载均衡,从不同的注册中心去获取服务,改成服务的名字
SpringCloud--3.ribbon负载均衡_第2张图片
(6)启动3个eureka,然后启动provider服务,在启动consumer服务,调用api看看,是否能访问到数据库:访问成功
SpringCloud--3.ribbon负载均衡_第3张图片

4.使用Ribbon实现负载均衡

(1)由于上面的demo只有一个服务提供者,体会不到服务端的负载均衡,所以我们多来两个服务提供者,那么现在我们需要多创建两个数据库,模拟分库分表(因为没有数据库的数据同步,所以现在只能模拟一下):导出springcloud_demo_db1的结构和数据,修改数据库名字等信息,然后运行sql语句,创建出新库
在这里插入图片描述
(2)然后再创建两个提供者的服务,指向不同的数据库,配置信息中要注意服务名称不能修改,这是前提,其他的要修改端口等信息
![在这里插入图片描述](https://img-blog.csdnimg.cn/0228a7e133c64b47a40bc110fd3d7056.png
(3)启动eureka(启动一个即可,不然太卡了),再启动三个provider服务,最后启动consumer服务,浏览器访问:
SpringCloud--3.ribbon负载均衡_第4张图片
发现是从第二个数据库获取的,也就是从8002端口的服务端获取的;我们再多访问几次看看
SpringCloud--3.ribbon负载均衡_第5张图片
自动地从第三个服务去获取了。我们还可以发现,它默认使用的负载均衡是轮询算法,因为每点一次就换下一个服务。

5.Ribbon负载均衡的算法有哪些?

首先我们要了解IRule接口,ribbon的负载均衡算法都来自于这个接口的实现类:

package com.netflix.loadbalancer;
public interface IRule {
    Server choose(Object var1);
    void setLoadBalancer(ILoadBalancer var1);
    ILoadBalancer getLoadBalancer();
}

这是他的实现类:
SpringCloud--3.ribbon负载均衡_第6张图片
常用的实现类的负载均衡算法
(1)RoundRobinRule:轮询策略,这是默认的策略
(2)AvailabilityFilteringRule:先过滤掉跳闸,访问故障的服务,再进行轮询可用的服务
(3)RandomRule:随机策略
(4)RetryRule:重试策略,先按照轮询进行服务获取,但获取失败后会在指定时间内重试
还有其他的策略不多介绍

由于默认的是轮询,上面也证实了,现在我们测试一下改成随机策略:我们在ConfigBean类下重新写一个bean,并注册到spring中SpringCloud--3.ribbon负载均衡_第7张图片
重启服务,同样访问http://localhost/consumer/dept/list,发现是随机获取服务的了,不进行轮询了。

6.自定义Ribbon负载均衡算法

上面说到,直接向spring注册即可,那我们自己写一个均衡的策略,然后注册到spring应该也是可以的。
(1)我们先写一个类MyRibbonRule,加上@Configuration注解,标识这是配置类,这个类用于写自定义的策略

在这种情况下,客户端由RibbonClientConfiguration中已经存在的组件与自定义的Configuration中的任何组件组成(后者通常会覆盖前者)。
自定义的Configuration必须是@Configuration(即使用这个注解),但请注意,它不在主应用程序上下文的@ComponentScan中,否则将由所有@RibbonClients共享(注意是@RibbonClients,不是说@RibbonClient)。如果您使用@ComponentScan (或@SpringBootApplication),则需要采取措施避免包含(例如将其放在一个单独的,不重重的包中,或者指定要在@ComponentScan )。

这就说明了我们的MyRibbonRule不能放在启动类的同级目录和子级目录下,因为这样会被@ComponentScan扫描到,我们自定义的这个配置类就会被所有的Ribbon客户端所共享(也就是所有的服务用同一个策略了),也就是说我们达不到特殊化定制的目的了;
这里我们放在了启动类的上一级:
SpringCloud--3.ribbon负载均衡_第8张图片
(2)由于更改了的配置,需要在启动类加上@RibbonClient注解,点进这个注解,发现可以配置3个信息:

@Configuration
@Import({RibbonClientConfigurationRegistrar.class})
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RibbonClient {
    String value() default "";

    String name() default "";//需要进行负载均衡的服务名字,默认可以不写

    Class<?>[] configuration() default {};//自定义的策略的类信息,默认用自带的
}

(3)给注解添上自定义的信息:

@RibbonClient(name = "SPRING-CLOUD-PROVIDER-DEPT",configuration = MyRibbonRule.class)
//在spring启动之后,就会加载自定义的类信息了

(4)写MyRuleImpl的内容,我们根据轮询策略来更改一下:每个服务提供3次再进行轮询

private int total=0;
    private int currentIndex=0;
    @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) {
                if (Thread.interrupted()) {
                    return null;
                }
                List<Server> upList = lb.getReachableServers();//存活的服务
                List<Server> allList = lb.getAllServers();//所有的服务
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
                if (total<3){//我们要在轮询的基础上,每一个服务都使用3次,
                    server=upList.get(currentIndex);
                    total++;
                }
                else {//已经满足3次,就要轮询下一个
                    currentIndex=(currentIndex+1)%upList.size();
                    server=upList.get(currentIndex);
                    total=1;
                }
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }
                    server = null;
                    Thread.yield();
                }
            }
            return server;
        }
    }

(5)在MyRibbonRule类中写一个返回MyRuleImpl的bean,并注册到spring

@Configuration
public class MyRibbonRule {
    //IRule 随机
    @Bean
    public IRule randomRule(){
        return new MyRuleImpl();
    }
}

(6)测试发现是按照代码那样执行,每访问3次,就换了下一个服务

但是其实这并不完全是正确的,因为在实践中就会发现,每次通过lb.getReachableServers()获取可达的服务,返回结果顺序并不是每次都相同的,有可能顺序是[1,2,3],也可能是[1,3,2],但是无伤大雅。

你可能感兴趣的:(springcloud,spring-boot,ribbon)