• springcloud 负载均衡之 ribbon。


    一、什么是    ribbon?
            
            就是负载均衡!
            nginx也是负载均衡
            
    1.1        !!!!ribbon和nginx的区别是什么?
    /*            nginx:
                    正向代理(和客户端连在一起)
                    反向代理(和服务器端连在一起),nginx的负载均衡其实使用的就是反向代理
                    
                ribbon:
                    负载均衡是和客户端连在一起的,也就是说ribbon是客户端层面的负载均衡
                    负载均衡放在客户端的好处是什么?
                        可以让客户端非常直观的看到所有服务器的负载情况,
                        那么客户端一般情况下会选择负载比较少的服务器进行连接(选择游戏大区)
    */

    二、consumer 开启负载均衡的配置

    1.2 ribbon默认自带的有eureka的jar包,也就是如果需要使用ribbon的时候就必须要配置eureka,
    
                也就是说需要把自己托管给eureka,ribbon是客户端层面的负载均衡,那么ribbon是配置在consumer中还是provider中?
                必须要配置在consumer上
                
                
    /*
        将    20190927-springcloud-consumer-6081    项目的jar包放在父级目录上,做jar包管理。
            我的放在
            
            新建项目 20190927-springcloud-consumer-ribbon-6082  做负载均衡。和另一项目一样,需要创建启动类,和配置文件。
            
    */    
    
    1.3 添加jar包
                添加eureka的client端,ribbon的jar包
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
                </dependency>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </dependency>

      20190927-springcloud-consumer-management  父级jar包展示。

    <?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>20190927-springcloud-parent</artifactId>
            <groupId>com.aaa</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>20190927-springcloud-consumer-management</artifactId>
    
        <packaging>pom</packaging>
    
        <modules>
            <module>20190927-springcloud-consumer-6081</module>
            <module>20190927-springcloud-consumer-ribbon-6082</module>
        </modules>
    
        <!--
        消费者 依赖于 model  和页面交互
        在20190927-springcloud-consumer-management项目下,建立多个子级项目,可以将这里的jar包直接放在
        父级的项目的xml文件中。
    
        我的放在这里 没有生效,还是在子级项目上添加的jar 包依赖。
    -->
    
        <dependencies>
            <dependency>
                <groupId>com.aaa</groupId>
                <artifactId>20190927-springcloud-model</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <!--
        微服务,将  spring-boot-starter-web添加进来
    
        需要修改默认的端口号。
     -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
    </project>
     20190927-springcloud-consumer-ribbon-6082 jar包依赖, 的配置文件,开启轮循,controller层。
    <?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>20190927-springcloud-consumer-management</artifactId>
            <groupId>com.aaa</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.aaa</groupId>
        <artifactId>20190927-springcloud-consumer-ribbon-6082</artifactId>
    
    <!--
        添加jar包
                添加eureka的client端,ribbon的jar包
    -->
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            </dependency>
    
    
            <dependency>
                <groupId>com.aaa</groupId>
                <artifactId>20190927-springcloud-model</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
    
            <!--
        微服务,将  spring-boot-starter-web添加进来
    
        需要修改默认的端口号。
     -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
        </dependencies>
    
    
    </project>
    
    
    



    1.配置文件
    server.port=6082
    server.servlet.context-path=/

    #三、 设置eureka 不要将自己注册到注册中心里面。
    eureka.client.register-with-eureka=false


    # ribbon 负载均衡的配置,需要将自己分别在三个 eureka上去注册。
    eureka.client.service-url.defaultZone=http://eureka01:7081/eureka,http://eureka02:7082/eureka,http://eureka03:7083/eureka
    2.开启轮循
    package com.aaa.zxf.config;
    
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class ConfigRest {
    
        @Bean
        @LoadBalanced       //开启负载均衡  轮循的模式
        public RestTemplate getRestTemplate(){
            return  new RestTemplate();
        }
    
    
    }
    
    
    
    
    
    3.controller层
    package com.aaa.zxf.controller;
    
    import com.aaa.zxf.model.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    
    @RestController
    public class UserController {
    
       //   private static final String URL = "http://USER-PROVIDER";
    
        private static final String URL ="http://USER-PROVIDER";
    
        @Autowired
        private RestTemplate restTemplate;
    
        @RequestMapping("/userAll")
        public List<User> selectAllUser(){
            return  restTemplate.getForObject(URL+"/userAll",List.class);
        }
    
    
    }
    
    
    
    
    
    

    三、provider 负载均衡的配置。

    
    
    1.4 真正的负载均衡
                需要创建provider-8082和8083项目
    
    1.5 进行配置
                8081,8082,8083三个项目中的所有的controller都必须要一致(@RequestMapping("/userAll"))
    8081,8082,8083项目的application.properties配置文件必须要更改
    【eureka的实例id:因为eureka的实例id是不能重复的,所以必须更改】 eureka.instance.instance
    -id=user-provider-8082

    spring.application.name不能改变,也就是说8081,8082,8083三个项目的application.name都必须要一样 1.6 启动顺序 首先启动三台eureka(7081,7082,7083) 再启动三台provider(8081,8082,8083) 最后启动consumer(6082)
    
    
    
    provider的配置文件,修改id  其他的都一样。从8081复制即可。

    #四
    #在eureka中配置实例, 就是eureka的status下显示的名字
    # 不可以重复!!! 等同于mysql中表的id。
    eureka.instance.instance-id=user-provider-8082

    #四
    #在eureka中配置实例, 就是eureka的status下显示的名字
    # 不可以重复!!! 等同于mysql中表的id。
    eureka.instance.instance-id=user-provider-8083
     

    四、负载均衡的源码分析?
    1.7 ribbon的负载均衡算法
                如果不指定ribbon的算法,则ribbon使用的是轮询!!!
                如何指定ribbon的算法?
    
    1.8 负载均衡源码解析
                IRule:接口
                ILoadBalancer:接口
                AbstractLoadBalancer(抽象类) --> 实现了ILoadBalancer接口
                AbstractLoadBalancerRule(抽象类) --> 实现了IRule接口
                    有一个属性:ILoadBalance-->getter/setter方法
                RandomRule(最终所实现随机算法的地方)--->继承了AbstractLoadBalancerRule
                    -->实现了IRule接口
                RoundRobbinRule--->继承了AbstractLoadBalancerRule
                    -->实现了IRule接口
    
                IRule:
                    public Server choose(Object key):选择出一个服务-->提供给consumer使用
                    public void setLoadBalancer(ILoadBalancer lb):ILoadBalancer-->实现类(AbstractLoadBalancer)
                    public ILoadBalancer getLoadBalancer()
                ILoadBalancer:
                    !!其实就是和每一个服务打交道!!
                    getServer(Object key):所有的获取服务都必须要通过key来进行获取
    
                虽然已经实现了ribbon算法自定义配置,但是ribbon框架是不知道的,但是自己已经实现了负载均衡算法,需要告诉ribbon,只需要在主启动类上添加注解@RibbonClient(name,configuration)
                    name:通过eureka的Application的值来映射到真正的provider(8081,8082,8083)
                    name = "USER-PROVIDER"
                    configuration:指向的是自己自定义的算法配置类
    
                    @Configuration
                    public class RibbonRuleMine {
    
                        @Bean
                        public IRule randoms() {
                            return new RandomRule();
                        }
    
                    }


    算法?
    package com.aaa.lee.springcloud.config;
    
    import com.netflix.loadbalancer.ILoadBalancer;
    
    import java.util.concurrent.ThreadLocalRandom;
    
    
    public class Test {
    
        // 随机算法
        public Server choose(ILoadBalancer lb, Object key) {
            // 如果负载均衡器为null的时候,直接返回(其实就是标识了该consumer开启了负载均衡@LoadBalance)
            if (lb == null) {
                return null;
            }
            // 创建Server对象,没有初始化
            Server server = null;
    
            // 循环进入条件是server为null-->也就是说第一次一定会进入循环
            while (server == null) {
                // Thread.interrupted():标识线程所处于的状态,如果线程的状态为存在,返回值就是true,如果不存在返回值就是false
                // 什么时候线程会断开??-->网络故障,网络延迟,各种阻塞
                if (Thread.interrupted()) {
                    return null;
                }
                // getReachableServers():获取当前正在存活的服务(一共有20个服务,其中有4个宕机,存活的服务就是16个)
                // getAllServers():获取所有的服务个数(包含宕机的)
                List<Server> upList = lb.getReachableServers();
                List<Server> allList = lb.getAllServers();
    
                // allList.size() == 0 说明没有服务,直接return null
                int serverCount = allList.size();
                if (serverCount == 0) {
                    return null;
                }
    
                // serverCount:就是所有服务的个数(包含宕机的服务)
                // 假设:30台
                int index = chooseRandomInt(serverCount);
                // upList:存活的服务
                server = upList.get(index);
    
                if (server == null) {
                
                    /**
                     * Thread.yield():线程谦让
                     *      什么叫多线程之间的谦让呢?
                     *          当并发出现的时候,线程和线程是互不相让的(线程阻塞),其中一个线程就会出现问题,就会处于等待状态(线程阻塞会更严重)
                     *          当调用yield()方法的时候,出现问题的线程就开始谦让,这个线程会再次被唤醒处于就绪状态,需要重新抢夺客户端所发送过来的并发
                     */
                    Thread.yield();
                    continue;
                }
    
                if (server.isAlive()) {
                    return (server);
                }
    
                // Shouldn't actually happen.. but must be transient or a bug.
                server = null;
                Thread.yield();
            }
    
            return server;
    
        }
    
        protected int chooseRandomInt(int serverCount) {
            // Math.random.nextInt(999):随机获取到从1到999的随机数
            // ThreadLocalRandom.current().nextInt(30):就是获取的是随机数(从1到30进行随机数)
            //
             /*
             * Random和ThreadLocalRandom都是随机数,那么为什么使用ThreadLocalRandom不使用Random?
             *  !!Random就是线程安全的!!
             *  ThreadLocalRandom:是jdk7的新特性!!
             *  虽然random是线程安全的,但是在多线程的情况,Random效率就会非常低
             *      random在处理多线程的情况下是线程安全的,会受到线程保护,就会降低效率(队列)
             *  Random所随机出来的数字是可以预测的(Random有规律)
             *
             *  TreadLocalRandom官方给出的解释,当项目期望在多线程中运行的时候,如果使用到了随机数,可以直接使用该类,因为该
             *  类就是针对于并发所开发的,和Random相比TreadLocalRandom可以减少线程之间的竞争,性能可以达到最优!
             *
             *
             */
            return ThreadLocalRandom.current().nextInt(serverCount);
        }
    
    }

    五、图解


    
    
    






  • 相关阅读:
    beacon帧字段结构最全总结(三)——VHT字段总结
    beacon帧字段结构最全总结(二)——HT字段总结
    [LeetCode]题53:Maximum Subarray
    [LeetCode]题15:3Sum
    dilated convolutions:扩张卷积
    python:assert
    [LeetCode]题1:two sum
    opencv3.0配置opencv_contrib
    python学习:数据类型
    python:字典嵌套列表
  • 原文地址:https://www.cnblogs.com/ZXF6/p/11618058.html
Copyright © 2020-2023  润新知