zoukankan      html  css  js  c++  java
  • SpringCloud笔记四:Ribbon

    什么是Ribbon?

    Ribbon是一个客户端的负载均衡。

    我举个例子就明白了,我去超市买东西付钱,收银台有3个,一个收银台有10个人排队,一个收银台有5个人排队,一个收银台有2个人排队。只要我不是傻子,我就知道我该去2个人排队的那个收银台。

    我是客户,我知道选择人最少的收银台。这就是客户端的负载均衡。这就是Ribbon

    提一句,负载均衡有两种方式:

    1. 集中式:这个就是有一个硬件,比如F5,提供者和消费者之间通过硬件负载均衡,缺点是硬件一般都很贵
    2. 进程内:这个是软件的形式,把负载均衡的逻辑放到消费方,让消费方根据逻辑去选择一台合适的服务器。

    Ribbon的配置

    Maven引入

    由于Ribbon是客户端的负载均衡,我们在consumer项目里面引入Maven配置

           <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-ribbon</artifactId>
                <version>1.4.6.RELEASE</version>
            </dependency>
    

    开启注解

    注解这有两个地方需要标注,我们的consumer是使用restTemplate来访问的,在获取restTemplate的方法上开启负载均衡注解,@LoadBalanced

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

    顺便,consumer里面的Controller里面,访问的地址我们原来写的是localhost,既然是微服务,我们肯定使用服务名称了,改一下

    //    private static final String REST_URL_PREFIX="http://localhost:8001";
        private static final String REST_URL_PREFIX="http://PROVIDER-DEPT";
    

    第二个注解就是主方法那里,加一个@EnableEurekaClient

    @SpringBootApplication
    @EnableEurekaClient
    public class Consumer80Application {
        public static void main(String[] args) {
            SpringApplication.run(Consumer80Application.class, args);
        }
    }
    

    再次重启启动eureka7001,eureka7002,eureka7003和provider,consumer,你会发现还是完全ok的。

    Ribbon负载均衡

    上面说了,Ribbon是客户端的负载均衡,所以我们要加在consumer项目上,Ribbon默认的负载均衡方式的轮询。我们可以新建两个provider来测试一下

    新建provider8002和8003

    新建项目就不多说了,记得把8001的yml,Mybatis,代码啥的复制过去。如下

    server:
      port: 8002
    
    mybatis:
      config-location: classpath:mybatis/mybatis.cfg.xml     #mybatis配置文件所在路径
      type-aliases-package: com.vae.springcloud.entity       #所有Entity别名类所在包
      mapper-locations: classpath:mybatis/mapper/**/*.xml    #mapper映射文件
    
    spring:
      application:
        name: provider-dept
      datasource:
    #   type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/vae?serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123
        dbcp2:
          min-idle: 5                                           # 数据库连接池的最小维持连接数
          initial-size: 5                                       # 初始化连接数
          max-total: 5                                          # 最大连接数
          max-wait-millis: 200                                 # 等待连接获取的最大超时时间
    
    eureka:
      client: #客户端注册进eureka服务列表内
        service-url:
    #      defaultZone: http://localhost:7001/eureka
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      instance:
        instance-id: provider-8002  #这个是修改Eureka界面的Status名称
        prefer-ip-address: true    #这个是设置鼠标放在status上的时候,出现的提示,设置ip地址显示
    
    
    info:
      app.name: provider-8001
      company.name: www.vae.com
      build.artifactId: $project.artifactId$
      build.version: $project.version$
    
    
    
    server:
      port: 8003
    
    mybatis:
      config-location: classpath:mybatis/mybatis.cfg.xml     #mybatis配置文件所在路径
      type-aliases-package: com.vae.springcloud.entity       #所有Entity别名类所在包
      mapper-locations: classpath:mybatis/mapper/**/*.xml    #mapper映射文件
    
    spring:
      application:
        name: provider-dept
      datasource:
    #   type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://localhost:3306/jj?serverTimezone=UTC
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: 123
        dbcp2:
          min-idle: 5                                           # 数据库连接池的最小维持连接数
          initial-size: 5                                       # 初始化连接数
          max-total: 5                                          # 最大连接数
          max-wait-millis: 200                                 # 等待连接获取的最大超时时间
    
    eureka:
      client: #客户端注册进eureka服务列表内
        service-url:
    #      defaultZone: http://localhost:7001/eureka
          defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      instance:
        instance-id: provider-8003  #这个是修改Eureka界面的Status名称
        prefer-ip-address: true    #这个是设置鼠标放在status上的时候,出现的提示,设置ip地址显示
    
    
    info:
      app.name: provider-8001
      company.name: www.vae.com
      build.artifactId: $project.artifactId$
      build.version: $project.version$
    
    
    

    看到没,yml文件,我只修改了端口号、数据库和instance-id状态id。因为每一个微服务都可以都一个独立的数据库,所以,三个provider的数据库我分别使用的是shuyunquan,vae,jj三个数据库,当然,表结构都是一样的没改

    代码直接复制就行,没什么需要改的。

    现在我们启动eureka7001,eureka7002,eureka7003,provider8001,provider8002,provider8003和consumer。算一下7个项目了,运行之后,你可以通过consumer访问试试,结果每次刷新都换了数据库,这刚好表明了Ribbon的默认负载均衡方式是轮询。

    Ribbon核心组件IRule

    Ribbon的核心组件IRule作用是定义以什么样的方式去负载均衡,比如轮询,比如随机。要想使用IRule,我们需要写一个类。

    注意:IRule组件的类不能在有@ComponentScan组件的类的包以及子包下存在,主方法的@SpringBootApplication注解里面有@ComponentScan注解,所以,我们不能在主方法的所在包或者子包下新建IRule组件

    我们在vae包下新建myrule包,包下新建MySelfRule类,代码如下:

    package com.vae.myrule;
    
    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MySelfRule {
    
        @Bean
        public IRule myRule(){
            return new RandomRule(); //这个是负载均衡的随机访问
    //      return new RoundRobinRule();  这个是负载均衡的默认的轮询访问
        }
    }
    
    

    看一下项目目录图

    Ribbon自定义

    上面讲的都太简单了,完全体现不出技术含量,所以Ribbon负载均衡的方式只有轮询和随机这还不够,我们要学会自定义负载均衡的方式,先看一张图

    要想自定义Ribbon负载均衡,我们要自己写个类,首先要继承AbstractLoadBalancerRule这个类,然后剩下的代码嘛,你可以去Ribbon的官方GitHub看看,Ribbon官方随机代码,把这里面的方法复制出来,我们看看里面的内容都是啥,我们只摘取方法

    package com.vae.myrule;
    
    import com.netflix.client.config.IClientConfig;
    import com.netflix.loadbalancer.AbstractLoadBalancerRule;
    import com.netflix.loadbalancer.ILoadBalancer;
    import com.netflix.loadbalancer.Server;
    
    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;
    
    
    public class RandomRule extends AbstractLoadBalancerRule {
    
    
     public Server choose(ILoadBalancer lb, Object key) {
         if (lb == null) {
             return null;
         }
         Server server = null;
    
         while (server == null) {
             //线程是否中断,中断返回null
             if (Thread.interrupted()) {
                 return null;
             }
             List<Server> upList = lb.getReachableServers();
             List<Server> allList = lb.getAllServers();
    
             int serverCount = allList.size();
             if (serverCount == 0) {
                 /*
                  * No servers. End regardless of pass, because subsequent passes
                  * only get more restrictive.
                  */
                 return null;
             }
    
             int index = chooseRandomInt(serverCount);
             server = upList.get(index);
    
             if (server == null) {
                 /*
                  * The only time this should happen is if the server list were
                  * somehow trimmed. This is a transient condition. Retry after
                  * yielding.
                  */
                 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) {
            return ThreadLocalRandom.current().nextInt(serverCount);
        }
    
        @Override
        public Server choose(Object key) {
            return choose(getLoadBalancer(), key);
        }
    
        @Override
        public void initWithNiwsConfig(IClientConfig iClientConfig) {
    
        }
    }
    
    
    
    

    可以看到啊,大概就是这样子的,initWithNiwsConfig方法是我们继承了AbstractLoadBalancerRule实现的,上面的方法中,很容易看出choose方法才是真正有内容的,我们现在就可以对这个随机算法进行修改了,比如,我想修改为每个服务访问5次,然后再随机的访问下一个服务。肯定是修改choose方法了

    private int total = 0; 			// 总共被调用的次数,目前要求每台被调用5次
    	private int currentIndex = 0;	// 当前提供服务的机器号
    
    	public Server choose(ILoadBalancer lb, Object key)
    	{
    		if (lb == null) {
    			return null;
    		}
    		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) {
    				/*
    				 * No servers. End regardless of pass, because subsequent passes only get more
    				 * restrictive.
    				 */
    				return null;
    			}
    
    //			int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
    //			server = upList.get(index);
    
    			
    //			private int total = 0; 			// 总共被调用的次数,目前要求每台被调用5次
    //			private int currentIndex = 0;	// 当前提供服务的机器号
                if(total < 5)
                {
    	            server = upList.get(currentIndex);
    	            total++;
                }else {
    	            total = 0;
    	            currentIndex++;
    	            if(currentIndex >= upList.size())
    	            {
    	              currentIndex = 0;
    	            }
                }			
    			
    			
    			if (server == null) {
    				/*
    				 * The only time this should happen is if the server list were somehow trimmed.
    				 * This is a transient condition. Retry after yielding.
    				 */
    				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;
    
    	}
    

    可以看到,我就定义了两个变量,自定义算法这个是很主观的,你需要什么样的方式来负载均衡就自己改代码就好了。

  • 相关阅读:
    QuickSort(Java)
    MergeSort(Java)
    Silverlight中Datagrid添加Button列用于控制单行对象
    二叉搜索树(BST)demo
    svn ignore使用方法
    海量数据处理面试题及解决方法
    Android中的单元测试
    Ubuntu下配置Intellij的Android开发环境
    urlrewrite地址重写之后丢失css和js解决方案
    修改后 简单的 TCP server
  • 原文地址:https://www.cnblogs.com/yunquan/p/10690528.html
Copyright © 2011-2022 走看看