springcloud入门(三)之Ribbon负载均衡及自定义负载均衡

springcloud入门(一)之基本概念、服务提供者及消费者

springcloud入门(二)之eureka服务发现与注册

springcloud入门(四)之feign负载均衡与rest调用

springcloud入门(五)之Hystrix熔断、降级及监控

springcloud入门(六)之Zuul路由网关及过滤

springcloud入门(七)之config配置中心

1 Ribbon负载均衡

1.1 ribbon是什么

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

1.2 ribbon能干什么

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

集中式LB

即在服务的消费方和提供方之间使用独立的LB设施,如Nginx,由该设施负责把访问请求通过某种策略转发至服务的提供方

进程式LB

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

1.3 ribbon实现负载均衡

以下是针对8080消费者工程的调整

pom

在8080消费者工程中新添加两个依赖

       
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-eurekaartifactId>
            <version>1.4.7.RELEASEversion>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-ribbonartifactId>
            <version>1.4.7.RELEASEversion>
        dependency>

RestTemplateConfig

目前使用的RestTemplate调用,需要添加注解

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

yml

添加eureka配置

server:
  port: 8080

# eureka配置
eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka0.com:8000/eureka/,http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/

启动类

@SpringBootApplication
@EnableEurekaClient //服务启动后注册在eureka上
public class ConsumerDept8080 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDept8080.class, args);
    }
}

DeptController

将调用地址该为服务名

@RestController
@RequestMapping("/dept")
public class DeptController {
    @Autowired
    private RestTemplate restTemplate;

    //用Ribbon做负载均衡的时候不应该写死地址,地址应该是一个变量,通过服务名来访问
    private static final String REST_URL_PREFIX = "http://PROVIDER-DEPT/dept/";
    //private static final String REST_URL_PREFIX = "http://localhost:8001/dept/";

    @GetMapping("/findAll")
    public List<Dept> findAll() {
        return restTemplate.getForObject(REST_URL_PREFIX + "findAllDept", List.class);
    }

    @GetMapping("/findById/{id}")
    public Dept findById(@PathVariable("id") int id) {
        return restTemplate.getForObject(REST_URL_PREFIX + "findById/" + id, Dept.class);
    }

    @PostMapping("/addDept")
    public int addDept(@RequestBody Dept dept) {
        return restTemplate.postForObject(REST_URL_PREFIX + "addDept", dept, Integer.class);
    }
}

自此 我们的负载均衡已经实现了,但是由于提供者只有一个,那么现实的结果也一样;那我们在分布创建8002和8003两个服务提供者

模拟数据库

整体架构图
为了便于对应这儿将8001的数据库对应为demo1
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第1张图片
创建三个数据库demo1、demo2及demo3 数据一样只是dept_source为数据库名以便区分
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第2张图片

创建另两个服务提供者

额外在新建两个模块 8002及8003 代码直接copy即可
只需要将配置对应调整

  • 三个服务提供者代码一致(启动类根据自己的端口命名)
  • pom导包一样
  • yml的端口对应数据库 instance-id对应(应用名称不能修改)

8001

server:
  port: 8001

mybatis:
  mapper-locations: classspath:mapper/*.xml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
  application:
    name: provider-dept

eureka:
  client:
    service-url:
      # 将原本注册在一台的 注册到三台上
      #defaultZone: http://localhost:8000/eureka/
      defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/
  instance:
    instance-id: springcloud-provider-8001 #修改在Eureka上默认的状态名字
info:
  app.name: Damon-springcloud
  company.name: www.ccct.com

8002

server:
  port: 8002

mybatis:
  mapper-locations: classspath:mapper/*.xml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo2?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
  application:
    name: provider-dept

eureka:
  client:
    service-url:
      # 将原本注册在一台的 注册到三台上
      #defaultZone: http://localhost:8000/eureka/
      defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/
  instance:
    instance-id: springcloud-provider-8002 #修改在Eureka上默认的状态名字
info:
  app.name: Damon-springcloud
  company.name: www.ccct.com

8003

server:
  port: 8003

mybatis:
  mapper-locations: classspath:mapper/*.xml

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/demo3?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&allowMultiQueries=true
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
  application:
    name: provider-dept

eureka:
  client:
    service-url:
      # 将原本注册在一台的 注册到三台上
      #defaultZone: http://localhost:8000/eureka/
      defaultZone: http://eureka1.com:8100/eureka/,http://eureka2.com:8200/eureka/,http://eureka0.com:8000/eureka/
  instance:
    instance-id: springcloud-provider-8003 #修改在Eureka上默认的状态名字
info:
  app.name: Damon-springcloud
  company.name: www.ccct.com

项目结构
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第3张图片
然后先启动三个注册中心集群;在启动三个提供者者;最后启动消费者
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第4张图片

访问打印

访问地址:http://localhost:8080/dept/findAll;多访问几次你会发现是轮询,三个提供者轮流提供数据
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第5张图片
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第6张图片
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第7张图片
从上述的截图信息中可以发现数据来源于不同的数据库

2 Ribbon负载均衡探究

2.1 IRule接口

Ribbon中有一个非常重要的接口IRule接口;该接口基本上实现了所有常用的负载均衡算法
springcloud入门(三)之Ribbon负载均衡及自定义负载均衡_第8张图片
里面有很多负载均衡算法,默认为轮询;如果需要别的算法,只需要要修改配置类中注册的Bean即可

RestTemplateConfig

@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced //Ribbon 只需要加了这个注解,这个RestTemplate就变成了负载均衡
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule(){
        //return new RoundRobinRule(); //默认轮询访问
        return new RandomRule(); //随机访问
    }
}

修改为上述配置后重启8080消费者项目,访问接口多访问几次;就会发现没有规律可循

2.2 自定义负载均衡算法

在配置包中新建负载均衡算法类MyRule

MyRule

复制一个实现算法,修改算法核心代码

public class MyRule extends AbstractLoadBalancerRule {
    //每个机器访问3次换下一个 总共3个

    private int total = 0;//被调用的次数
    private int curIndex = 0;//当前是谁在提供服务

    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){
                    total++;
                }else {
                    total = 0;
                    curIndex++;
                    if(curIndex>=serverCount){
                        curIndex=0;
                    }
                }
                server = upList.get(curIndex);
                //---------核心代码----------
                
                if (server == null) {
                    Thread.yield();
                } else {
                    if (server.isAlive()) {
                        return server;
                    }

                    server = null;
                    Thread.yield();
                }
            }
            return server;
        }
    }

    @Override
    public void initWithNiwsConfig(IClientConfig iClientConfig) {

    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(),key);
    }
}

RestTemplateConfig

修改注册bean

 @Bean
    public IRule myRule(){
      	//return new RoundRobinRule(); //默认轮询访问
        //return new RandomRule(); //随机访问
        return new MyRule();
    }

启动类

添加注解,让项目启动时加载我们自定义的规则即可

@SpringBootApplication
@EnableEurekaClient //服务启动后注册在eureka上
//项目启动时添加我们自定义的规则
@RibbonClient(name = "PROVIDER-DEPT",configuration= MyRule.class)
public class ConsumerDept8080 {
    public static void main(String[] args) {
        SpringApplication.run(ConsumerDept8080.class, args);
    }
}

重启消费者8080项目,访问http://localhost:8080/dept/findAll;你会发现每3次就会变化数据来源。

你可能感兴趣的:(springcloud,ribbon,spring,cloud,负载均衡)