《五》、springcloud微服务——Ribbon负载均衡

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端的 负载均衡的工具。
Ribbon源码:https://github.com/Netflix/ribbon

一、Ribbon的配置初步

1、修改 microservicecloud-consumer-dept-80 工程

(1)、修改pom.xml文件
   添加Ribbon 相关依赖

      <!-- Ribbon相关 -->
       <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>

(2)、修改application.yml文件 追加eureka的服务注册地址

server:
  port: 80
  
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)、对ConfigBean进行添加新注解@Load Balanced 获得Rest时加入Ribbon的配置

@Configuration   //配置类,类似spring里面的applicationContext.xml
public class ConfigBean {

    /**
     * RestTemplate提供了多种便捷访问远程Http服务的方法
      *     是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
      *    使用restTemplate访问restful接口非常的简单粗暴无脑。
     *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
     *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
     */
    @Bean //注入bean
    @LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具。 开启负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

(4)、主启动类DeptConsumer80_App.java添加注解@EnableEurekaClient

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

(5)、修改DeptController_Consumer.java客户端访问类

package com.smilexl.springcloud.controller;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import com.smilexl.springcloud.entities.Dept;

@RestController
public class DeptController_Consumer {
    
    /**
     * RestTemplate提供了多种便捷访问远程Http服务的方法
      *   是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
      *   使用restTemplate访问restful接口非常的简单粗暴无脑。
     *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
     *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
     */
    
//  private static final String REST_URL_PREFIX = "http://localhost:8001";
    private static final String REST_URL_PREFIX = "http://SERVICESPRINGCLOUD-DEPT";  //改为微服务名字

    @Autowired
    private RestTemplate restTemplate;

    // 添加
    @GetMapping("/consumer/dept/add")
    public Boolean add(Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class);
    }

    // 根据id查询
    @GetMapping("/consumer/dept/get/{id}")
    public Dept get(@PathVariable("id") Long id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class);
    }

    // 根据id查询
    @SuppressWarnings("unchecked")
    @GetMapping("/consumer/dept/list")
    public List<Dept> list() {
        return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class);
    }
}

(6)、先启动3个eureka集群,再启动microservicecloud-provider-dept-8001并注册进eureka

(7)、启动microservicecloud-consumer-dept-80,测试

(8)、小总结

Ribbon和Rureka整合后Cousumer可以直接调用服务而不用再关心地址和端口号


二、Ribbon负载均衡

1、架构说明

Ribbon在工作时分成两步
  第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server,
  第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址,
  其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

2、参考microservicecloud-provider-dept-8001,新建8002、8003
  • pom.xml文件中添加如下依赖
  <dependencies>
        <dependency><!-- 引入自己定义的api通用包,可以使用Dept部门Entity -->
            <groupId>cn.smilexl.springcloud</groupId>
            <artifactId>microservicecloud-api</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jetty</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        
        <!-- 将微服务provider注册进eureka -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- actuator监控信息完善 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
3、新建8002、8003数据库,各微服务分别连自己的数据库
  • 8002 SQL脚本
DROP DATABASE IF EXISTS cloudDB02;
CREATE DATABASE cloudDB02 CHARACTER SET UTF8;
USE cloudDB02;
CREATE TABLE `dept` (
  `deptno` bigint(20) NOT NULL AUTO_INCREMENT,
  `dname` varchar(60) DEFAULT NULL,
  `db_source` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

INSERT INTO `dept` VALUES ('1', '开发部', 'clouddb01');
INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
INSERT INTO `dept` VALUES ('3', '财务部', 'clouddb01');
INSERT INTO `dept` VALUES ('4', '市场部', 'clouddb01');
INSERT INTO `dept` VALUES ('5', '运维部', 'clouddb01');
INSERT INTO `dept` VALUES ('6', '资讯部', 'clouddb01');
  • 8003 SQL脚本
DROP DATABASE IF EXISTS cloudDB03;
CREATE DATABASE cloudDB03 CHARACTER SET UTF8;
USE cloudDB03;
CREATE TABLE `dept` (
  `deptno` bigint(20) NOT NULL AUTO_INCREMENT,
  `dname` varchar(60) DEFAULT NULL,
  `db_source` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`deptno`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

INSERT INTO `dept` VALUES ('1', '开发部', 'clouddb01');
INSERT INTO `dept` VALUES ('2', '人事部', 'clouddb01');
INSERT INTO `dept` VALUES ('3', '财务部', 'clouddb01');
INSERT INTO `dept` VALUES ('4', '市场部', 'clouddb01');
INSERT INTO `dept` VALUES ('5', '运维部', 'clouddb01');
INSERT INTO `dept` VALUES ('6', '资讯部', 'clouddb01');
4、修改8002、8003各子的yml
  • 8002 的yml
server:
  port: 8002
  
mybatis:
#  config-location: classpath:mybatis/mybatis.cfg.xml       # mybatis配置文件所在路径
  type-aliases-package: cn.smilexl.springcloud.entities     # 所有Entity别名类所在包
  mapper-locations: classpath:mapper/**/*.xml               # mapper映射文件
    
spring:
   application:
    name: microservicecloud-dept                           # 微服务名
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://47.99.218.123:3306/clouddb02          # 数据库名称
    username: root
    password: 123456
    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: microservicecloud-dept-8002  #自定义服务名称信息
    prefer-ip-address: true  #访问路径可以显示IP地址
 
#点击超链接后显示信息   
info:
  app.name: microservicecloud
  company.name: www.smilexl.cn
  build.artifactId: $project.artifactId$
  build.version: $project.version$
   
  • 8003 的yml
server:
  port: 8003
  
mybatis:
#  config-location: classpath:mybatis/mybatis.cfg.xml       # mybatis配置文件所在路径
  type-aliases-package: cn.smilexl.springcloud.entities     # 所有Entity别名类所在包
  mapper-locations: classpath:mapper/**/*.xml               # mapper映射文件
    
spring:
   application:
    name: microservicecloud-dept                           # 微服务名
   datasource:
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型
    driver-class-name: org.gjt.mm.mysql.Driver              # mysql驱动包
    url: jdbc:mysql://47.99.218.123:3306/clouddb03          # 数据库名称
    username: root
    password: 123456
    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: microservicecloud-dept-8003  #自定义服务名称信息
    prefer-ip-address: true  #访问路径可以显示IP地址
 
 #点击超链接后显示信息   
info:
  app.name: microservicecloud
  company.name: www.smilexl.cn
  build.artifactId: $project.artifactId$
  build.version: $project.version$
  

注意:

5、启动3个Dept微服务并且各自测试通过
  http://localhost:8001/dept/list
  http://localhost:8002/dept/list
  http://localhost:8003/dept/list
6、启动 microservicecloud-consumer-dept-80
  http://localhost/consumer/dept/list

客户端通过Ribbon完成负载均衡。注意观看返回的数据库名字,各不相同,负载均衡实现

7、总结

   Ribbon其实就是一个负载均衡的客户端组件,他可以和其他所需要请求的客户端结合使用,和eureka结合只是其中的一个实例。

三、Ribbon核心组件IRule

IRule:根据特定算法中从服务列表中选取一个要访问的服务

  • RoundRobinRule:轮询;
  • RandomRule:随机;
  • AvailabilityFilteringRule:会过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
  • WeightedResponseTimeRule: 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时如果统计信息不足,则使用RoundTobinRule策略,等统计信息足够会切换到WeightedResponseTimeRule;
  • RetryRule: 先按照RoudRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用服务,如果还是失败的就会自动把这个失败的服务隔离掉;
  • BestAvailableRule: 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量小的服务;
  • ZoneAvoidanceRule: 默认规则,复合判断server所在区域的性能和server的可用性选择服务器;

修改Ribbon的算法:修改 microservicecloud-consumer-dept-80 中 /cfgbeans/ConfigBean.java文件

@Configuration   //配置类,类似spring里面的applicationContext.xml
public class ConfigBean {
    /**
     * RestTemplate提供了多种便捷访问远程Http服务的方法
      *     是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
      *    使用restTemplate访问restful接口非常的简单粗暴无脑。
     *  (url, requestMap, ResponseBean.class)这三个参数分别代表 
     *  REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。
     */
    @Bean //注入bean
    @LoadBalanced //Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端,负载均衡的工具。 开启负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
    
    @Bean
    public IRule myRule() {
        return new RandomRule();//达到的目的:用我们从新选择的随机算法代替默认的轮询算法
    }
    
}
//相对于,<bean id="restTemplate" class="com.smilexl.springcloud.cfgbean.RestTemplate">
    

四、Ribbon 自定义算法

1、在 microservicecloud-consumer-dept-80 主启动类上添加 @RibbonClient
package cn.smilexl.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import cn.smilexl.myrule.MyselfRule;

@SpringBootApplication
@EnableEurekaClient
//在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效。
@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)
public class DeptConsumer80_App {
    
    public static void main(String[] args) {
        SpringApplication.run(DeptConsumer80_App.class, args);
    }
}

@RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MyselfRule.class)

  • name:微服务的名字
  • configuration:自定义的算法类
    意思:对微服务 MICROSERVICECLOUD-DEPT,使用 MyselfRule.java类中的规则

注意配置事项: 这个自定义类(MyselfRule.java)不能放在@ComponentScan(即:主启动类)所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

2、步骤

(1)、新建package cn.smilexl.myrule 包

(2)、在此包下新建自定义Ribbon规则类 MyselfRule.java

package cn.smilexl.myrule;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;

@Configuration
public class MyselfRule {
    
    @Bean
    public IRule myRule() {
        return new RandomRule();//Ribbon默认为轮询,自定义为随机
    }
}
3、Ribbon自定义规则深度解析

需求:依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每台机器一次,现在是每台机器5次。

解析源码:https://github.com/Netflix/ribbon/blob/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer/RandomRule.java

参考源码修改为需求要求的 RoundRobinRule_LL

(1)、参考源码修改为需要的算法策略(RoundRobinRule_LL.java)

package cn.smilexl.myrule;

import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;

/**
 * 自定义需求规则类
 *  --依旧轮询策略,但是加上新需求,每个服务器要求被调用5次。也即以前是每台机器一次,现在是每台机器5次
 */

public class RoundRobinRule_LL extends AbstractLoadBalancerRule {

    /**
     * Randomly choose from all living servers
     */
    
    private int total = 0; //总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0; //当前提供服务的机器号
    
    @SuppressWarnings("unused")
    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 = chooseRandomInt(serverCount);
//            server = upList.get(index);   
            //定义判断规则
            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;
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }
    
    @Override
    public void initWithNiwsConfig(IClientConfig arg0) {
        // TODO Auto-generated method stub
    }
}

(2)、在配置类中注入自定义的算法策略(MyselfRule.java)

package cn.smilexl.myrule;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;

@Configuration
public class MyselfRule {
    
    @Bean
    public IRule myRule() {
//      return new RandomRule();//Ribbon默认为轮询,自定义为随机
//      return new RoundRobinRule();//轮询
        return new RoundRobinRule_LL(); //调用自定义的算法规则,每台轮询调用5次
    }
}

(3)、最终工程目录结构如下:

(4)、依次启动工程并测试结果

依次启动工程:
microservicecloud-eureka-7001;
microservicecloud-eureka-7002;
microservicecloud-eureka-7003;
microservicecloud-provider-dept-8001;
microservicecloud-provider-dept-8002;
microservicecloud-provider-dept-8003;
microservicecloud-consumer-dept-80;

测试:localhost/consumer/dept/get/1

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容