负载均衡工具--Ribbon使用浅析

18 篇文章 0 订阅
16 篇文章 0 订阅

负载均衡分为软负载均衡、硬负载均衡、DNS负载均衡。

先上一图:

区别看这:几种负载均衡分类 - 简书 (jianshu.com)

Ribbon是软负载均衡的客户端工具。用于客户端的负载均衡。

补充:Nginx是软负载均衡的服务器端工具。用于服务器端的负载均衡。

Ribbon实现负载均衡:

        Ribbon是软负载均衡的客户端工具。所以肯定是用在客户端了,即消费者端。注意:消费者端也需要注册进Eureka。

使用前提:已经实现通过服务名来访问服务。即已经完成服务注册与服务发现。

代码:

生产者端application.yml代码:

server:
  port: 8083

spring:
  application:
    name: ProviderCRUD
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/mybatis_plus_test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true&serverTimezone=UTC
      username: root
      password: root
      initial-size: 5
      min-idle: 5
      max-active: 20
      #最大超时时间 ms
      max-wait: 60000
      #设置多长时间进行一次检测 ms
      time-between-eviction-runs-millis: 60000
      #配置一个连接在连接池中最小的生存时间 ms
      min-evictable-idle-time-millis: 300000
      validation-query: select 1 from dual
      test-on-borrow: false
      test-on-return: false
      test-while-idle: false
      #打开PSCache,并指定每个连接上的PSCache大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      #配置监控拦截器,去掉后监控界面sql无法显示  wall用于防火墙
      filters: stat,slf4j,wall,log4j
      #通过connectProperties属性打开mergeSql功能,可记录慢sql  ms
      connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      #合并多个DruidDataSource的监控记录
      use-global-data-source-stat: true
    type: com.alibaba.druid.pool.DruidDataSource

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.log4j.Log4jImpl

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8082/eureka/,http://127.0.0.1:8084/eureka/,http://127.0.0.1:8085/eureka/
  instance:
    #自定义服务名称
    instance-id: SpringCloudTestSGG-ProviderCRUD1
    #访问路径是否显示IP地址
    prefer-ip-address: true



生产者端controller代码:

@RestController
@RequestMapping("/user")
public class UserController {

    @Resource
    private UserMapper userMapper;
    @Resource
    private UserService userService;
    @Autowired
    private DiscoveryClient discoveryClient;

    @GetMapping("/getAllUser")
    public String getAllUser(){
        List<User> users = userMapper.selectList(null);
        String jsonString = JSONObject.toJSONString(users);
        return jsonString;
    }


    //服务发现
    @RequestMapping("/discovery")
    public Object discovery(){

        List<String> services = discoveryClient.getServices();
        System.out.println("***********"+services);

        List<ServiceInstance> instances = discoveryClient.getInstances("PROVIDERCRUD");
        for (ServiceInstance instance : instances) {
            System.out.println(instance.getHost()+"\t"+instance.getServiceId()+"\t"+instance.getPort()+"\t"+instance.getUri());
        }
        return this.discoveryClient;
    }
}

生产者端启动类代码:

@EnableEurekaClient //本服务启动后,自动注册进Eureka
@SpringBootApplication
@EnableDiscoveryClient //服务发现
public class SpringCloudEurekaClient {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEurekaClient.class, args);
    }
}

使用Ribbon负载均衡的时候,生产者启动类上一定要加上@EnableDiscoveryClient 注解,表示服务可以被发现。

既然是负载均衡那肯定不止一个生产者端了,多建几个,都注册进Eureka。

生产者端application.yml中配置代码几乎都是一样的。

主要区别在端口号一定不同,要不然端口冲突,服务启动不起来。

因为每一个服务都可以有自己的数据库,所以数据库方面的配置可能不同。

服务实例名spring.application.name一定是相同的,因为是通过服务名来负载均衡,所以必须保证一样。

在消费者端或者说客户端Maven中引入Ribbon依赖

  

    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-ribbon -->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-ribbon</artifactId>
      <version>1.4.7.RELEASE</version>
    </dependency>

消费者端application.yml配置相对简单,因为负责请求的转发,调用其他服务,所以不需要向Eureka中注册自己

server:
  port: 8086

eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8082/eureka/,http://127.0.0.1:8084/eureka/,http://127.0.0.1:8085/eureka/
    #表示不把自己注册到Eureka
    register-with-eureka: false
  instance:
    instance-id: SpringCloudTestSGG-Consumer1
    prefer-ip-address: true


spring:
  application:
    name: ConsumerCRUD

消费者端RestTemplate配置代码:

@Configuration
public class RestConfig {

    @Bean
    @LoadBalanced //负载均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

RestTemplate配置类中一定要添加@LoadBalanced 负载均衡注解。

消费者端controller代码:

@RestController
@RequestMapping("/rest")
public class UserRestController {

    @Resource
    private RestTemplate restTemplate;

    //通过服务名来进行访问服务,这里演示用
    //PROVIDERCRUD服务名字
    private static String PREFIX_URL="http://PROVIDERCRUD/user/";

    @GetMapping("/getAllUser")
    public String getAllUser(){
       return restTemplate.getForObject(PREFIX_URL+"getAllUser",String.class);
    }

    @RequestMapping("/discovery")
    public Object discovery(){
        return restTemplate.getForObject(PREFIX_URL+"discovery",Object.class);
    }
}

这样就可以简单实现Ribbon的负载均衡了。

Ribbon默认负载均衡的策略是轮询。就是一个一个的轮流访问服务。

下图是Ribbon自带的负载均衡策略。

切换负载均衡策略:Ribbon的IRule接口:

IRule源码:

/*
*
* Copyright 2013 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.netflix.loadbalancer;

/**
 * Interface that defines a "Rule" for a LoadBalancer. A Rule can be thought of
 * as a Strategy for loadbalacing. Well known loadbalancing strategies include
 * Round Robin, Response Time based etc.
 * 
 * @author stonse
 * 
 */
public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

切换策略代码:

@Configuration
public class IRuleConfig {

    @Bean
    public IRule iRule(){

        /**
         * RoundRobinRule
         * 轮询
         */
        return new RoundRobinRule();


        /**
         * RandomRule
         * 随机
         */
        return new RandomRule();


        /**
         * AvailabilityFilteringRule
         * 过滤访问故障和超过访问或并发阈值的服务。在剩下的服务中轮询。
         */
        return new AvailabilityFilteringRule();


        /**
         * WeightedResponseTimeRule
         * 按照权重访问。权重计算方法:响应时间越快的权重越高,被选中的概率越大。刚启动时如果统计信息不足,先按轮询策略,
         *  后续统计信息足够了再按照权重策略。
         */
        return new WeightedResponseTimeRule();

        /**
         * RetryRule
         * 先按照轮询策略访问服务,如果获取服务失败,则会在指定时间内进行重试,重新获取服务。
         */
        return new RetryRule();

        /**
         * BestAvailableRule
         * 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
         */
        return new BestAvailableRule();

        /**
         * ZoneAvoidanceRule
         * 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
         */
        return new ZoneAvoidanceRule();
    }
}

实现自定义的Ribbon负载均衡策略:

实现IRule接口:

步骤:在客户端即消费者端启动类上添加  

        @RibbonClient(name = "xxx",configuration=yyy.class)  注解,

        name表示执行此策略的服务名;configuration表示自定义负载策略的类。

简而言之就是针对名为xxx的服务,执行yyy.class中的负载策略。

注意:

 也就是说,自定义的负载均策略类不能和启动类在同一个包或者父包下。

消费者启动类:

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "PROVIDERCRUD",configuration= IRuleConfig.class)
public class SpringCloudConsumer {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudConsumer.class,args);
    }
}

MyRuleConfig,自定义负载算法类:

/**
 * 自定义负载策略
 * 要求:轮询服务,并且每个服务连续五次
 */

public class MySelfIRule extends AbstractLoadBalancerRule {

    private Logger log;
    private Integer total = 0;
    private int index = 0;
    public MySelfIRule() {
        System.out.println("执行无参构造方法");
    }

    public MySelfIRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
        System.out.println("执行有参构造方法");
    }

    Server server = null;

    public Server choose(ILoadBalancer lb,Object key) {
        System.out.println("执行双参数choose查找返回server");
        if (lb==null){
            log.log(Level.WARNING,"没有对应的服务...");
        }

        List<Server> allServers = lb.getAllServers();
        if (allServers.size()==0){
            log.log(Level.WARNING,"没有指定的>>>"+lb+"<<<服务");
            return null;
        }

        if (total<4){//0,1,2,3,4
            server = allServers.get(index);
            total++;
        }else {//5
            total=0;
            index++;
            if (index>=allServers.size()){
                index=0;
            }
        }

        if (server==null){
            return null;
        }
        return server;
    }


    public Server choose(Object key) {
        System.out.println("执行单参数choose");
        return choose(getLoadBalancer(),key);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
        System.out.println("执行初始化方法initWithNiwsConfig,获取服务信息...");

        String clientName = clientConfig.getClientName();
        System.out.println("获取服务名>>:"+clientName);

        System.out.println("获取配置信息...");
        Map<String, Object> properties = clientConfig.getProperties();
        for (Map.Entry<String, Object> stringObjectEntry : properties.entrySet()) {
            System.out.println(stringObjectEntry);
        }
    }

}

在切换策略代码中,直接切换即可:

@Configuration
public class IRuleConfig {

    @Bean
    public IRule iRule(){


        /**
         * 自定义
         */
        return new MySelfIRule();
    }
}

到此完成自定义的Ribbon负载均衡算法。

Nginx负载均衡待更新。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值